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 "SkRRect.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 */
66
67class CircleGeometryProcessor : public GrGeometryProcessor {
68public:
69    CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
70                            const SkMatrix& localMatrix)
71            : INHERITED(kCircleGeometryProcessor_ClassID)
72            , fLocalMatrix(localMatrix) {
73        fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
74        fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
75        fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kFloat4_GrVertexAttribType);
76        if (clipPlane) {
77            fInClipPlane = &this->addVertexAttrib("inClipPlane", kHalf3_GrVertexAttribType);
78        } else {
79            fInClipPlane = nullptr;
80        }
81        if (isectPlane) {
82            fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kHalf3_GrVertexAttribType);
83        } else {
84            fInIsectPlane = nullptr;
85        }
86        if (unionPlane) {
87            fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kHalf3_GrVertexAttribType);
88        } else {
89            fInUnionPlane = nullptr;
90        }
91        fStroke = stroke;
92    }
93
94    ~CircleGeometryProcessor() override {}
95
96    const char* name() const override { return "CircleEdge"; }
97
98    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
99        GLSLProcessor::GenKey(*this, caps, b);
100    }
101
102    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
103        return new GLSLProcessor();
104    }
105
106private:
107    class GLSLProcessor : public GrGLSLGeometryProcessor {
108    public:
109        GLSLProcessor() {}
110
111        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
112            const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
113            GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
114            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
115            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
116            GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
117
118            // emit attributes
119            varyingHandler->emitAttributes(cgp);
120            fragBuilder->codeAppend("float4 circleEdge;");
121            varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
122            if (cgp.fInClipPlane) {
123                fragBuilder->codeAppend("half3 clipPlane;");
124                varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
125            }
126            if (cgp.fInIsectPlane) {
127                SkASSERT(cgp.fInClipPlane);
128                fragBuilder->codeAppend("half3 isectPlane;");
129                varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
130            }
131            if (cgp.fInUnionPlane) {
132                SkASSERT(cgp.fInClipPlane);
133                fragBuilder->codeAppend("half3 unionPlane;");
134                varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
135            }
136
137            // setup pass through color
138            varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
139
140            // Setup position
141            this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
142
143            // emit transforms
144            this->emitTransforms(vertBuilder,
145                                 varyingHandler,
146                                 uniformHandler,
147                                 cgp.fInPosition->asShaderVar(),
148                                 cgp.fLocalMatrix,
149                                 args.fFPCoordTransformHandler);
150
151            fragBuilder->codeAppend("float d = length(circleEdge.xy);");
152            fragBuilder->codeAppend("half distanceToOuterEdge = circleEdge.z * (1.0 - d);");
153            fragBuilder->codeAppend("half edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
154            if (cgp.fStroke) {
155                fragBuilder->codeAppend(
156                        "half distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
157                fragBuilder->codeAppend("half innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
158                fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
159            }
160
161            if (cgp.fInClipPlane) {
162                fragBuilder->codeAppend(
163                        "half clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
164                        "clipPlane.z, 0.0, 1.0);");
165                if (cgp.fInIsectPlane) {
166                    fragBuilder->codeAppend(
167                            "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
168                            "isectPlane.z, 0.0, 1.0);");
169                }
170                if (cgp.fInUnionPlane) {
171                    fragBuilder->codeAppend(
172                            "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
173                            "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
174                }
175                fragBuilder->codeAppend("edgeAlpha *= clip;");
176            }
177            fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
178        }
179
180        static void GenKey(const GrGeometryProcessor& gp,
181                           const GrShaderCaps&,
182                           GrProcessorKeyBuilder* b) {
183            const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
184            uint16_t key;
185            key = cgp.fStroke ? 0x01 : 0x0;
186            key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
187            key |= cgp.fInClipPlane ? 0x04 : 0x0;
188            key |= cgp.fInIsectPlane ? 0x08 : 0x0;
189            key |= cgp.fInUnionPlane ? 0x10 : 0x0;
190            b->add32(key);
191        }
192
193        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
194                     FPCoordTransformIter&& transformIter) override {
195            this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
196                                         pdman, &transformIter);
197        }
198
199    private:
200        typedef GrGLSLGeometryProcessor INHERITED;
201    };
202
203    SkMatrix fLocalMatrix;
204    const Attribute* fInPosition;
205    const Attribute* fInColor;
206    const Attribute* fInCircleEdge;
207    const Attribute* fInClipPlane;
208    const Attribute* fInIsectPlane;
209    const Attribute* fInUnionPlane;
210    bool fStroke;
211
212    GR_DECLARE_GEOMETRY_PROCESSOR_TEST
213
214    typedef GrGeometryProcessor INHERITED;
215};
216
217GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
218
219#if GR_TEST_UTILS
220sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
221    return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
222            d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(),
223            d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
224}
225#endif
226
227///////////////////////////////////////////////////////////////////////////////
228
229/**
230 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
231 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
232 * in both x and y directions.
233 *
234 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
235 */
236
237class EllipseGeometryProcessor : public GrGeometryProcessor {
238public:
239    EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
240    : INHERITED(kEllipseGeometryProcessor_ClassID)
241    , fLocalMatrix(localMatrix) {
242        fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
243        fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
244        fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kHalf2_GrVertexAttribType);
245        fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kHalf4_GrVertexAttribType);
246        fStroke = stroke;
247    }
248
249    ~EllipseGeometryProcessor() override {}
250
251    const char* name() const override { return "EllipseEdge"; }
252
253    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
254        GLSLProcessor::GenKey(*this, caps, b);
255    }
256
257    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
258        return new GLSLProcessor();
259    }
260
261private:
262    class GLSLProcessor : public GrGLSLGeometryProcessor {
263    public:
264        GLSLProcessor() {}
265
266        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
267            const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
268            GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
269            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
270            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
271
272            // emit attributes
273            varyingHandler->emitAttributes(egp);
274
275            GrGLSLVarying ellipseOffsets(kHalf2_GrSLType);
276            varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
277            vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
278                                     egp.fInEllipseOffset->fName);
279
280            GrGLSLVarying ellipseRadii(kHalf4_GrSLType);
281            varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
282            vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName);
283
284            GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
285            // setup pass through color
286            varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
287
288            // Setup position
289            this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
290
291            // emit transforms
292            this->emitTransforms(vertBuilder,
293                                 varyingHandler,
294                                 uniformHandler,
295                                 egp.fInPosition->asShaderVar(),
296                                 egp.fLocalMatrix,
297                                 args.fFPCoordTransformHandler);
298
299            // for outer curve
300            fragBuilder->codeAppendf("half2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
301                                     ellipseRadii.fsIn());
302            fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
303            fragBuilder->codeAppendf("half2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
304            fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
305
306            // avoid calling inversesqrt on zero.
307            fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
308            fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
309            fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
310
311            // for inner curve
312            if (egp.fStroke) {
313                fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(),
314                                         ellipseRadii.fsIn());
315                fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
316                fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn());
317                fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
318                fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
319            }
320
321            fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
322        }
323
324        static void GenKey(const GrGeometryProcessor& gp,
325                           const GrShaderCaps&,
326                           GrProcessorKeyBuilder* b) {
327            const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
328            uint16_t key = egp.fStroke ? 0x1 : 0x0;
329            key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
330            b->add32(key);
331        }
332
333        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
334                     FPCoordTransformIter&& transformIter) override {
335            const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
336            this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
337        }
338
339    private:
340        typedef GrGLSLGeometryProcessor INHERITED;
341    };
342
343    const Attribute* fInPosition;
344    const Attribute* fInColor;
345    const Attribute* fInEllipseOffset;
346    const Attribute* fInEllipseRadii;
347    SkMatrix fLocalMatrix;
348    bool fStroke;
349
350    GR_DECLARE_GEOMETRY_PROCESSOR_TEST
351
352    typedef GrGeometryProcessor INHERITED;
353};
354
355GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
356
357#if GR_TEST_UTILS
358sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
359    return sk_sp<GrGeometryProcessor>(
360            new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
361}
362#endif
363
364///////////////////////////////////////////////////////////////////////////////
365
366/**
367 * The output of this effect is a modulation of the input color and coverage for an ellipse,
368 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
369 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
370 * using differentials.
371 *
372 * The result is device-independent and can be used with any affine matrix.
373 */
374
375enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
376
377class DIEllipseGeometryProcessor : public GrGeometryProcessor {
378public:
379    DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
380            : INHERITED(kDIEllipseGeometryProcessor_ClassID)
381            , fViewMatrix(viewMatrix) {
382        fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
383        fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
384        fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kHalf2_GrVertexAttribType);
385        fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kHalf2_GrVertexAttribType);
386        fStyle = style;
387    }
388
389    ~DIEllipseGeometryProcessor() override {}
390
391    const char* name() const override { return "DIEllipseEdge"; }
392
393    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
394        GLSLProcessor::GenKey(*this, caps, b);
395    }
396
397    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
398        return new GLSLProcessor();
399    }
400
401private:
402    class GLSLProcessor : public GrGLSLGeometryProcessor {
403    public:
404        GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
405
406        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
407            const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
408            GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
409            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
410            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
411
412            // emit attributes
413            varyingHandler->emitAttributes(diegp);
414
415            GrGLSLVarying offsets0(kHalf2_GrSLType);
416            varyingHandler->addVarying("EllipseOffsets0", &offsets0);
417            vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName);
418
419            GrGLSLVarying offsets1(kHalf2_GrSLType);
420            varyingHandler->addVarying("EllipseOffsets1", &offsets1);
421            vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName);
422
423            GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
424            varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
425
426            // Setup position
427            this->writeOutputPosition(vertBuilder,
428                                      uniformHandler,
429                                      gpArgs,
430                                      diegp.fInPosition->fName,
431                                      diegp.fViewMatrix,
432                                      &fViewMatrixUniform);
433
434            // emit transforms
435            this->emitTransforms(vertBuilder,
436                                 varyingHandler,
437                                 uniformHandler,
438                                 diegp.fInPosition->asShaderVar(),
439                                 args.fFPCoordTransformHandler);
440
441            // for outer curve
442            fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn());
443            fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
444            fragBuilder->codeAppendf("half2 duvdx = dFdx(%s);", offsets0.fsIn());
445            fragBuilder->codeAppendf("half2 duvdy = dFdy(%s);", offsets0.fsIn());
446            fragBuilder->codeAppendf(
447                    "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
448                    "                  2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
449                    offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
450
451            fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
452            // avoid calling inversesqrt on zero.
453            fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
454            fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
455            if (DIEllipseStyle::kHairline == diegp.fStyle) {
456                // can probably do this with one step
457                fragBuilder->codeAppend("half edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
458                fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
459            } else {
460                fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
461            }
462
463            // for inner curve
464            if (DIEllipseStyle::kStroke == diegp.fStyle) {
465                fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
466                fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
467                fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
468                fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
469                fragBuilder->codeAppendf(
470                        "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
471                        "             2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
472                        offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
473                fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
474                fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
475            }
476
477            fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
478        }
479
480        static void GenKey(const GrGeometryProcessor& gp,
481                           const GrShaderCaps&,
482                           GrProcessorKeyBuilder* b) {
483            const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
484            uint16_t key = static_cast<uint16_t>(diegp.fStyle);
485            key |= ComputePosKey(diegp.fViewMatrix) << 10;
486            b->add32(key);
487        }
488
489        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
490                     FPCoordTransformIter&& transformIter) override {
491            const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
492
493            if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
494                fViewMatrix = diegp.fViewMatrix;
495                float viewMatrix[3 * 3];
496                GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
497                pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
498            }
499            this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
500        }
501
502    private:
503        SkMatrix fViewMatrix;
504        UniformHandle fViewMatrixUniform;
505
506        typedef GrGLSLGeometryProcessor INHERITED;
507    };
508
509    const Attribute* fInPosition;
510    const Attribute* fInColor;
511    const Attribute* fInEllipseOffsets0;
512    const Attribute* fInEllipseOffsets1;
513    SkMatrix fViewMatrix;
514    DIEllipseStyle fStyle;
515
516    GR_DECLARE_GEOMETRY_PROCESSOR_TEST
517
518    typedef GrGeometryProcessor INHERITED;
519};
520
521GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
522
523#if GR_TEST_UTILS
524sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
525    return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
526            GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
527}
528#endif
529
530///////////////////////////////////////////////////////////////////////////////
531
532// We have two possible cases for geometry for a circle:
533
534// In the case of a normal fill, we draw geometry for the circle as an octagon.
535static const uint16_t gFillCircleIndices[] = {
536        // enter the octagon
537        // clang-format off
538        0, 1, 8, 1, 2, 8,
539        2, 3, 8, 3, 4, 8,
540        4, 5, 8, 5, 6, 8,
541        6, 7, 8, 7, 0, 8
542        // clang-format on
543};
544
545// For stroked circles, we use two nested octagons.
546static const uint16_t gStrokeCircleIndices[] = {
547        // enter the octagon
548        // clang-format off
549        0, 1,  9, 0, 9,   8,
550        1, 2, 10, 1, 10,  9,
551        2, 3, 11, 2, 11, 10,
552        3, 4, 12, 3, 12, 11,
553        4, 5, 13, 4, 13, 12,
554        5, 6, 14, 5, 14, 13,
555        6, 7, 15, 6, 15, 14,
556        7, 0,  8, 7,  8, 15,
557        // clang-format on
558};
559
560
561static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
562static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
563static const int kVertsPerStrokeCircle = 16;
564static const int kVertsPerFillCircle = 9;
565
566static int circle_type_to_vert_count(bool stroked) {
567    return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
568}
569
570static int circle_type_to_index_count(bool stroked) {
571    return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
572}
573
574static const uint16_t* circle_type_to_indices(bool stroked) {
575    return stroked ? gStrokeCircleIndices : gFillCircleIndices;
576}
577
578///////////////////////////////////////////////////////////////////////////////
579
580class CircleOp final : public GrMeshDrawOp {
581private:
582    using Helper = GrSimpleMeshDrawOpHelper;
583
584public:
585    DEFINE_OP_CLASS_ID
586
587    /** Optional extra params to render a partial arc rather than a full circle. */
588    struct ArcParams {
589        SkScalar fStartAngleRadians;
590        SkScalar fSweepAngleRadians;
591        bool fUseCenter;
592    };
593
594    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
595                                          SkPoint center, SkScalar radius, const GrStyle& style,
596                                          const ArcParams* arcParams = nullptr) {
597        SkASSERT(circle_stays_circle(viewMatrix));
598        if (style.hasPathEffect()) {
599            return nullptr;
600        }
601        const SkStrokeRec& stroke = style.strokeRec();
602        SkStrokeRec::Style recStyle = stroke.getStyle();
603        if (arcParams) {
604            // Arc support depends on the style.
605            switch (recStyle) {
606                case SkStrokeRec::kStrokeAndFill_Style:
607                    // This produces a strange result that this op doesn't implement.
608                    return nullptr;
609                case SkStrokeRec::kFill_Style:
610                    // This supports all fills.
611                    break;
612                case SkStrokeRec::kStroke_Style:  // fall through
613                case SkStrokeRec::kHairline_Style:
614                    // Strokes that don't use the center point are supported with butt cap.
615                    if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
616                        return nullptr;
617                    }
618                    break;
619            }
620        }
621        return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style,
622                                               arcParams);
623    }
624
625    CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
626             SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams)
627            : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
628        const SkStrokeRec& stroke = style.strokeRec();
629        SkStrokeRec::Style recStyle = stroke.getStyle();
630
631        viewMatrix.mapPoints(&center, 1);
632        radius = viewMatrix.mapRadius(radius);
633        SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
634
635        bool isStrokeOnly =
636                SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
637        bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
638
639        SkScalar innerRadius = -SK_ScalarHalf;
640        SkScalar outerRadius = radius;
641        SkScalar halfWidth = 0;
642        if (hasStroke) {
643            if (SkScalarNearlyZero(strokeWidth)) {
644                halfWidth = SK_ScalarHalf;
645            } else {
646                halfWidth = SkScalarHalf(strokeWidth);
647            }
648
649            outerRadius += halfWidth;
650            if (isStrokeOnly) {
651                innerRadius = radius - halfWidth;
652            }
653        }
654
655        // The radii are outset for two reasons. First, it allows the shader to simply perform
656        // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
657        // Second, the outer radius is used to compute the verts of the bounding box that is
658        // rendered and the outset ensures the box will cover all partially covered by the circle.
659        outerRadius += SK_ScalarHalf;
660        innerRadius -= SK_ScalarHalf;
661        bool stroked = isStrokeOnly && innerRadius > 0.0f;
662        fViewMatrixIfUsingLocalCoords = viewMatrix;
663
664        // This makes every point fully inside the intersection plane.
665        static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
666        // This makes every point fully outside the union plane.
667        static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
668        SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
669                                            center.fX + outerRadius, center.fY + outerRadius);
670        if (arcParams) {
671            // The shader operates in a space where the circle is translated to be centered at the
672            // origin. Here we compute points on the unit circle at the starting and ending angles.
673            SkPoint startPoint, stopPoint;
674            startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
675            SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
676            stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
677
678            // Adjust the start and end points based on the view matrix (to handle rotated arcs)
679            startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
680            stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
681            startPoint.normalize();
682            stopPoint.normalize();
683
684            // If the matrix included scale (on one axis) we need to swap our start and end points
685            if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
686                SkTSwap(startPoint, stopPoint);
687            }
688
689            // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
690            // radial lines. However, in both cases we have to be careful about the half-circle.
691            // case. In that case the two radial lines are equal and so that edge gets clipped
692            // twice. Since the shared edge goes through the center we fall back on the useCenter
693            // case.
694            bool useCenter =
695                    (arcParams->fUseCenter || isStrokeOnly) &&
696                    !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI);
697            if (useCenter) {
698                SkVector norm0 = {startPoint.fY, -startPoint.fX};
699                SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
700                if (arcParams->fSweepAngleRadians > 0) {
701                    norm0.negate();
702                } else {
703                    norm1.negate();
704                }
705                fClipPlane = true;
706                if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
707                    fCircles.emplace_back(Circle{
708                            color,
709                            innerRadius,
710                            outerRadius,
711                            {norm0.fX, norm0.fY, 0.5f},
712                            {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
713                            {norm1.fX, norm1.fY, 0.5f},
714                            devBounds,
715                            stroked});
716                    fClipPlaneIsect = false;
717                    fClipPlaneUnion = true;
718                } else {
719                    fCircles.emplace_back(Circle{
720                            color,
721                            innerRadius,
722                            outerRadius,
723                            {norm0.fX, norm0.fY, 0.5f},
724                            {norm1.fX, norm1.fY, 0.5f},
725                            {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
726                            devBounds,
727                            stroked});
728                    fClipPlaneIsect = true;
729                    fClipPlaneUnion = false;
730                }
731            } else {
732                // We clip to a secant of the original circle.
733                startPoint.scale(radius);
734                stopPoint.scale(radius);
735                SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
736                norm.normalize();
737                if (arcParams->fSweepAngleRadians > 0) {
738                    norm.negate();
739                }
740                SkScalar d = -norm.dot(startPoint) + 0.5f;
741
742                fCircles.emplace_back(
743                        Circle{color,
744                               innerRadius,
745                               outerRadius,
746                               {norm.fX, norm.fY, d},
747                               {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
748                               {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
749                               devBounds,
750                               stroked});
751                fClipPlane = true;
752                fClipPlaneIsect = false;
753                fClipPlaneUnion = false;
754            }
755        } else {
756            fCircles.emplace_back(
757                    Circle{color,
758                           innerRadius,
759                           outerRadius,
760                           {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
761                           {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
762                           {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
763                           devBounds,
764                           stroked});
765            fClipPlane = false;
766            fClipPlaneIsect = false;
767            fClipPlaneUnion = false;
768        }
769        // Use the original radius and stroke radius for the bounds so that it does not include the
770        // AA bloat.
771        radius += halfWidth;
772        this->setBounds(
773                {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
774                HasAABloat::kYes, IsZeroArea::kNo);
775        fVertCount = circle_type_to_vert_count(stroked);
776        fIndexCount = circle_type_to_index_count(stroked);
777        fAllFill = !stroked;
778    }
779
780    const char* name() const override { return "CircleOp"; }
781
782    void visitProxies(const VisitProxyFunc& func) const override {
783        fHelper.visitProxies(func);
784    }
785
786    SkString dumpInfo() const override {
787        SkString string;
788        for (int i = 0; i < fCircles.count(); ++i) {
789            string.appendf(
790                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
791                    "InnerRad: %.2f, OuterRad: %.2f\n",
792                    fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
793                    fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
794                    fCircles[i].fInnerRadius, fCircles[i].fOuterRadius);
795        }
796        string += fHelper.dumpInfo();
797        string += INHERITED::dumpInfo();
798        return string;
799    }
800
801    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
802                                GrPixelConfigIsClamped dstIsClamped) override {
803        GrColor* color = &fCircles.front().fColor;
804        return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
805                                            GrProcessorAnalysisCoverage::kSingleChannel, color);
806    }
807
808    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
809
810private:
811    void onPrepareDraws(Target* target) override {
812        SkMatrix localMatrix;
813        if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
814            return;
815        }
816
817        // Setup geometry processor
818        sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
819                !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix));
820
821        struct CircleVertex {
822            SkPoint fPos;
823            GrColor fColor;
824            SkPoint fOffset;
825            SkScalar fOuterRadius;
826            SkScalar fInnerRadius;
827            // These planes may or may not be present in the vertex buffer.
828            SkScalar fHalfPlanes[3][3];
829        };
830
831        size_t vertexStride = gp->getVertexStride();
832        SkASSERT(vertexStride ==
833                 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
834                         (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
835                         (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)));
836
837        const GrBuffer* vertexBuffer;
838        int firstVertex;
839        char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
840                                                        &firstVertex);
841        if (!vertices) {
842            SkDebugf("Could not allocate vertices\n");
843            return;
844        }
845
846        const GrBuffer* indexBuffer = nullptr;
847        int firstIndex = 0;
848        uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
849        if (!indices) {
850            SkDebugf("Could not allocate indices\n");
851            return;
852        }
853
854        int currStartVertex = 0;
855        for (const auto& circle : fCircles) {
856            SkScalar innerRadius = circle.fInnerRadius;
857            SkScalar outerRadius = circle.fOuterRadius;
858            GrColor color = circle.fColor;
859            const SkRect& bounds = circle.fDevBounds;
860
861            CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
862            CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
863            CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
864            CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
865            CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
866            CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
867            CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
868            CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
869
870            // The inner radius in the vertex data must be specified in normalized space.
871            innerRadius = innerRadius / outerRadius;
872
873            SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
874            SkScalar halfWidth = 0.5f * bounds.width();
875            SkScalar octOffset = 0.41421356237f;  // sqrt(2) - 1
876
877            v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
878            v0->fColor = color;
879            v0->fOffset = SkPoint::Make(-octOffset, -1);
880            v0->fOuterRadius = outerRadius;
881            v0->fInnerRadius = innerRadius;
882
883            v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
884            v1->fColor = color;
885            v1->fOffset = SkPoint::Make(octOffset, -1);
886            v1->fOuterRadius = outerRadius;
887            v1->fInnerRadius = innerRadius;
888
889            v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
890            v2->fColor = color;
891            v2->fOffset = SkPoint::Make(1, -octOffset);
892            v2->fOuterRadius = outerRadius;
893            v2->fInnerRadius = innerRadius;
894
895            v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
896            v3->fColor = color;
897            v3->fOffset = SkPoint::Make(1, octOffset);
898            v3->fOuterRadius = outerRadius;
899            v3->fInnerRadius = innerRadius;
900
901            v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
902            v4->fColor = color;
903            v4->fOffset = SkPoint::Make(octOffset, 1);
904            v4->fOuterRadius = outerRadius;
905            v4->fInnerRadius = innerRadius;
906
907            v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
908            v5->fColor = color;
909            v5->fOffset = SkPoint::Make(-octOffset, 1);
910            v5->fOuterRadius = outerRadius;
911            v5->fInnerRadius = innerRadius;
912
913            v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
914            v6->fColor = color;
915            v6->fOffset = SkPoint::Make(-1, octOffset);
916            v6->fOuterRadius = outerRadius;
917            v6->fInnerRadius = innerRadius;
918
919            v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
920            v7->fColor = color;
921            v7->fOffset = SkPoint::Make(-1, -octOffset);
922            v7->fOuterRadius = outerRadius;
923            v7->fInnerRadius = innerRadius;
924
925            if (fClipPlane) {
926                memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
927                memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
928                memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
929                memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
930                memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
931                memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
932                memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
933                memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
934            }
935            int unionIdx = 1;
936            if (fClipPlaneIsect) {
937                memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
938                memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
939                memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
940                memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
941                memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
942                memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
943                memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
944                memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
945                unionIdx = 2;
946            }
947            if (fClipPlaneUnion) {
948                memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
949                memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
950                memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
951                memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
952                memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
953                memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
954                memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
955                memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
956            }
957
958            if (circle.fStroked) {
959                // compute the inner ring
960                CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
961                CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
962                CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
963                CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
964                CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
965                CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
966                CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
967                CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
968
969                // cosine and sine of pi/8
970                SkScalar c = 0.923579533f;
971                SkScalar s = 0.382683432f;
972                SkScalar r = circle.fInnerRadius;
973
974                v0->fPos = center + SkPoint::Make(-s * r, -c * r);
975                v0->fColor = color;
976                v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
977                v0->fOuterRadius = outerRadius;
978                v0->fInnerRadius = innerRadius;
979
980                v1->fPos = center + SkPoint::Make(s * r, -c * r);
981                v1->fColor = color;
982                v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
983                v1->fOuterRadius = outerRadius;
984                v1->fInnerRadius = innerRadius;
985
986                v2->fPos = center + SkPoint::Make(c * r, -s * r);
987                v2->fColor = color;
988                v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
989                v2->fOuterRadius = outerRadius;
990                v2->fInnerRadius = innerRadius;
991
992                v3->fPos = center + SkPoint::Make(c * r, s * r);
993                v3->fColor = color;
994                v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
995                v3->fOuterRadius = outerRadius;
996                v3->fInnerRadius = innerRadius;
997
998                v4->fPos = center + SkPoint::Make(s * r, c * r);
999                v4->fColor = color;
1000                v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
1001                v4->fOuterRadius = outerRadius;
1002                v4->fInnerRadius = innerRadius;
1003
1004                v5->fPos = center + SkPoint::Make(-s * r, c * r);
1005                v5->fColor = color;
1006                v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
1007                v5->fOuterRadius = outerRadius;
1008                v5->fInnerRadius = innerRadius;
1009
1010                v6->fPos = center + SkPoint::Make(-c * r, s * r);
1011                v6->fColor = color;
1012                v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
1013                v6->fOuterRadius = outerRadius;
1014                v6->fInnerRadius = innerRadius;
1015
1016                v7->fPos = center + SkPoint::Make(-c * r, -s * r);
1017                v7->fColor = color;
1018                v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
1019                v7->fOuterRadius = outerRadius;
1020                v7->fInnerRadius = innerRadius;
1021
1022                if (fClipPlane) {
1023                    memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1024                    memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1025                    memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1026                    memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1027                    memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1028                    memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1029                    memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1030                    memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1031                }
1032                int unionIdx = 1;
1033                if (fClipPlaneIsect) {
1034                    memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1035                    memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1036                    memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1037                    memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1038                    memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1039                    memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1040                    memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1041                    memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1042                    unionIdx = 2;
1043                }
1044                if (fClipPlaneUnion) {
1045                    memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1046                    memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1047                    memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1048                    memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1049                    memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1050                    memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1051                    memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1052                    memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1053                }
1054            } else {
1055                // filled
1056                CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1057                v8->fPos = center;
1058                v8->fColor = color;
1059                v8->fOffset = SkPoint::Make(0, 0);
1060                v8->fOuterRadius = outerRadius;
1061                v8->fInnerRadius = innerRadius;
1062                if (fClipPlane) {
1063                    memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1064                }
1065                int unionIdx = 1;
1066                if (fClipPlaneIsect) {
1067                    memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1068                    unionIdx = 2;
1069                }
1070                if (fClipPlaneUnion) {
1071                    memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1072                }
1073            }
1074
1075            const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1076            const int primIndexCount = circle_type_to_index_count(circle.fStroked);
1077            for (int i = 0; i < primIndexCount; ++i) {
1078                *indices++ = primIndices[i] + currStartVertex;
1079            }
1080
1081            currStartVertex += circle_type_to_vert_count(circle.fStroked);
1082            vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
1083        }
1084
1085        GrMesh mesh(GrPrimitiveType::kTriangles);
1086        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1087        mesh.setVertexData(vertexBuffer, firstVertex);
1088        target->draw(gp.get(),  fHelper.makePipeline(target), mesh);
1089    }
1090
1091    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1092        CircleOp* that = t->cast<CircleOp>();
1093
1094        // can only represent 65535 unique vertices with 16-bit indices
1095        if (fVertCount + that->fVertCount > 65536) {
1096            return false;
1097        }
1098
1099        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1100            return false;
1101        }
1102
1103        if (fHelper.usesLocalCoords() &&
1104            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
1105            return false;
1106        }
1107
1108        // Because we've set up the ops that don't use the planes with noop values
1109        // we can just accumulate used planes by later ops.
1110        fClipPlane |= that->fClipPlane;
1111        fClipPlaneIsect |= that->fClipPlaneIsect;
1112        fClipPlaneUnion |= that->fClipPlaneUnion;
1113
1114        fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
1115        this->joinBounds(*that);
1116        fVertCount += that->fVertCount;
1117        fIndexCount += that->fIndexCount;
1118        fAllFill = fAllFill && that->fAllFill;
1119        return true;
1120    }
1121
1122    struct Circle {
1123        GrColor fColor;
1124        SkScalar fInnerRadius;
1125        SkScalar fOuterRadius;
1126        SkScalar fClipPlane[3];
1127        SkScalar fIsectPlane[3];
1128        SkScalar fUnionPlane[3];
1129        SkRect fDevBounds;
1130        bool fStroked;
1131    };
1132
1133    SkMatrix fViewMatrixIfUsingLocalCoords;
1134    Helper fHelper;
1135    SkSTArray<1, Circle, true> fCircles;
1136    int fVertCount;
1137    int fIndexCount;
1138    bool fAllFill;
1139    bool fClipPlane;
1140    bool fClipPlaneIsect;
1141    bool fClipPlaneUnion;
1142
1143    typedef GrMeshDrawOp INHERITED;
1144};
1145
1146///////////////////////////////////////////////////////////////////////////////
1147
1148class EllipseOp : public GrMeshDrawOp {
1149private:
1150    using Helper = GrSimpleMeshDrawOpHelper;
1151
1152    struct DeviceSpaceParams {
1153        SkPoint fCenter;
1154        SkScalar fXRadius;
1155        SkScalar fYRadius;
1156        SkScalar fInnerXRadius;
1157        SkScalar fInnerYRadius;
1158    };
1159
1160public:
1161    DEFINE_OP_CLASS_ID
1162
1163    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1164                                          const SkRect& ellipse, const SkStrokeRec& stroke) {
1165        DeviceSpaceParams params;
1166        // do any matrix crunching before we reset the draw state for device coords
1167        params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1168        viewMatrix.mapPoints(&params.fCenter, 1);
1169        SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1170        SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1171        params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1172                                      viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1173        params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1174                                      viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
1175
1176        // do (potentially) anisotropic mapping of stroke
1177        SkVector scaledStroke;
1178        SkScalar strokeWidth = stroke.getWidth();
1179        scaledStroke.fX = SkScalarAbs(
1180                strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1181        scaledStroke.fY = SkScalarAbs(
1182                strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
1183
1184        SkStrokeRec::Style style = stroke.getStyle();
1185        bool isStrokeOnly =
1186                SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
1187        bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1188
1189        params.fInnerXRadius = 0;
1190        params.fInnerYRadius = 0;
1191        if (hasStroke) {
1192            if (SkScalarNearlyZero(scaledStroke.length())) {
1193                scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1194            } else {
1195                scaledStroke.scale(SK_ScalarHalf);
1196            }
1197
1198            // we only handle thick strokes for near-circular ellipses
1199            if (scaledStroke.length() > SK_ScalarHalf &&
1200                (0.5f * params.fXRadius > params.fYRadius ||
1201                 0.5f * params.fYRadius > params.fXRadius)) {
1202                return nullptr;
1203            }
1204
1205            // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1206            if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1207                        (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1208                scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1209                        (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
1210                return nullptr;
1211            }
1212
1213            // this is legit only if scale & translation (which should be the case at the moment)
1214            if (isStrokeOnly) {
1215                params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1216                params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
1217            }
1218
1219            params.fXRadius += scaledStroke.fX;
1220            params.fYRadius += scaledStroke.fY;
1221        }
1222        return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke);
1223    }
1224
1225    EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1226              const DeviceSpaceParams& params, const SkStrokeRec& stroke)
1227            : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1228        SkStrokeRec::Style style = stroke.getStyle();
1229        bool isStrokeOnly =
1230                SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
1231
1232        fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1233                                       params.fInnerXRadius, params.fInnerYRadius,
1234                                       SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1235                                                        params.fCenter.fY - params.fYRadius,
1236                                                        params.fCenter.fX + params.fXRadius,
1237                                                        params.fCenter.fY + params.fYRadius)});
1238
1239        this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
1240
1241        // Outset bounds to include half-pixel width antialiasing.
1242        fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1243
1244        fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1245        fViewMatrixIfUsingLocalCoords = viewMatrix;
1246    }
1247
1248    const char* name() const override { return "EllipseOp"; }
1249
1250    void visitProxies(const VisitProxyFunc& func) const override {
1251        fHelper.visitProxies(func);
1252    }
1253
1254    SkString dumpInfo() const override {
1255        SkString string;
1256        string.appendf("Stroked: %d\n", fStroked);
1257        for (const auto& geo : fEllipses) {
1258            string.appendf(
1259                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1260                    "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1261                    geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1262                    geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1263                    geo.fInnerYRadius);
1264        }
1265        string += fHelper.dumpInfo();
1266        string += INHERITED::dumpInfo();
1267        return string;
1268    }
1269
1270    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1271                                GrPixelConfigIsClamped dstIsClamped) override {
1272        GrColor* color = &fEllipses.front().fColor;
1273        return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1274                                            GrProcessorAnalysisCoverage::kSingleChannel, color);
1275    }
1276
1277    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1278
1279private:
1280    void onPrepareDraws(Target* target) override {
1281        SkMatrix localMatrix;
1282        if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1283            return;
1284        }
1285
1286        // Setup geometry processor
1287        sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
1288
1289        QuadHelper helper;
1290        size_t vertexStride = gp->getVertexStride();
1291        SkASSERT(vertexStride == sizeof(EllipseVertex));
1292        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1293                helper.init(target, vertexStride, fEllipses.count()));
1294        if (!verts) {
1295            return;
1296        }
1297
1298        for (const auto& ellipse : fEllipses) {
1299            GrColor color = ellipse.fColor;
1300            SkScalar xRadius = ellipse.fXRadius;
1301            SkScalar yRadius = ellipse.fYRadius;
1302
1303            // Compute the reciprocals of the radii here to save time in the shader
1304            SkScalar xRadRecip = SkScalarInvert(xRadius);
1305            SkScalar yRadRecip = SkScalarInvert(yRadius);
1306            SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
1307            SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
1308
1309            // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1310            SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1311            SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1312
1313            // The inner radius in the vertex data must be specified in normalized space.
1314            verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
1315            verts[0].fColor = color;
1316            verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
1317            verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1318            verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1319
1320            verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
1321            verts[1].fColor = color;
1322            verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
1323            verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1324            verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1325
1326            verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
1327            verts[2].fColor = color;
1328            verts[2].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
1329            verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1330            verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1331
1332            verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
1333            verts[3].fColor = color;
1334            verts[3].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
1335            verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1336            verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1337
1338            verts += kVerticesPerQuad;
1339        }
1340        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
1341    }
1342
1343    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1344        EllipseOp* that = t->cast<EllipseOp>();
1345
1346        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1347            return false;
1348        }
1349
1350        if (fStroked != that->fStroked) {
1351            return false;
1352        }
1353
1354        if (fHelper.usesLocalCoords() &&
1355            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
1356            return false;
1357        }
1358
1359        fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
1360        this->joinBounds(*that);
1361        return true;
1362    }
1363
1364    struct Ellipse {
1365        GrColor fColor;
1366        SkScalar fXRadius;
1367        SkScalar fYRadius;
1368        SkScalar fInnerXRadius;
1369        SkScalar fInnerYRadius;
1370        SkRect fDevBounds;
1371    };
1372
1373    SkMatrix fViewMatrixIfUsingLocalCoords;
1374    Helper fHelper;
1375    bool fStroked;
1376    SkSTArray<1, Ellipse, true> fEllipses;
1377
1378    typedef GrMeshDrawOp INHERITED;
1379};
1380
1381/////////////////////////////////////////////////////////////////////////////////////////////////
1382
1383class DIEllipseOp : public GrMeshDrawOp {
1384private:
1385    using Helper = GrSimpleMeshDrawOpHelper;
1386
1387    struct DeviceSpaceParams {
1388        SkPoint fCenter;
1389        SkScalar fXRadius;
1390        SkScalar fYRadius;
1391        SkScalar fInnerXRadius;
1392        SkScalar fInnerYRadius;
1393        DIEllipseStyle fStyle;
1394    };
1395
1396public:
1397    DEFINE_OP_CLASS_ID
1398
1399    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1400                                          const SkRect& ellipse, const SkStrokeRec& stroke) {
1401        DeviceSpaceParams params;
1402        params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1403        params.fXRadius = SkScalarHalf(ellipse.width());
1404        params.fYRadius = SkScalarHalf(ellipse.height());
1405
1406        SkStrokeRec::Style style = stroke.getStyle();
1407        params.fStyle = (SkStrokeRec::kStroke_Style == style)
1408                                ? DIEllipseStyle::kStroke
1409                                : (SkStrokeRec::kHairline_Style == style)
1410                                          ? DIEllipseStyle::kHairline
1411                                          : DIEllipseStyle::kFill;
1412
1413        params.fInnerXRadius = 0;
1414        params.fInnerYRadius = 0;
1415        if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1416            SkScalar strokeWidth = stroke.getWidth();
1417
1418            if (SkScalarNearlyZero(strokeWidth)) {
1419                strokeWidth = SK_ScalarHalf;
1420            } else {
1421                strokeWidth *= SK_ScalarHalf;
1422            }
1423
1424            // we only handle thick strokes for near-circular ellipses
1425            if (strokeWidth > SK_ScalarHalf &&
1426                (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
1427                 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
1428                return nullptr;
1429            }
1430
1431            // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1432            if (strokeWidth * (params.fYRadius * params.fYRadius) <
1433                (strokeWidth * strokeWidth) * params.fXRadius) {
1434                return nullptr;
1435            }
1436            if (strokeWidth * (params.fXRadius * params.fXRadius) <
1437                (strokeWidth * strokeWidth) * params.fYRadius) {
1438                return nullptr;
1439            }
1440
1441            // set inner radius (if needed)
1442            if (SkStrokeRec::kStroke_Style == style) {
1443                params.fInnerXRadius = params.fXRadius - strokeWidth;
1444                params.fInnerYRadius = params.fYRadius - strokeWidth;
1445            }
1446
1447            params.fXRadius += strokeWidth;
1448            params.fYRadius += strokeWidth;
1449        }
1450        if (DIEllipseStyle::kStroke == params.fStyle &&
1451            (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
1452            params.fStyle = DIEllipseStyle::kFill;
1453        }
1454        return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix);
1455    }
1456
1457    DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
1458                const SkMatrix& viewMatrix)
1459            : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1460        // This expands the outer rect so that after CTM we end up with a half-pixel border
1461        SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1462        SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1463        SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1464        SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1465        SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1466        SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
1467
1468        fEllipses.emplace_back(
1469                Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
1470                        params.fInnerYRadius, geoDx, geoDy, params.fStyle,
1471                        SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
1472                                         params.fCenter.fY - params.fYRadius - geoDy,
1473                                         params.fCenter.fX + params.fXRadius + geoDx,
1474                                         params.fCenter.fY + params.fYRadius + geoDy)});
1475        this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
1476                                   IsZeroArea::kNo);
1477    }
1478
1479    const char* name() const override { return "DIEllipseOp"; }
1480
1481    void visitProxies(const VisitProxyFunc& func) const override {
1482        fHelper.visitProxies(func);
1483    }
1484
1485    SkString dumpInfo() const override {
1486        SkString string;
1487        for (const auto& geo : fEllipses) {
1488            string.appendf(
1489                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1490                    "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1491                    "GeoDY: %.2f\n",
1492                    geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
1493                    geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1494                    geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
1495        }
1496        string += fHelper.dumpInfo();
1497        string += INHERITED::dumpInfo();
1498        return string;
1499    }
1500
1501    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1502                                GrPixelConfigIsClamped dstIsClamped) override {
1503        GrColor* color = &fEllipses.front().fColor;
1504        return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1505                                            GrProcessorAnalysisCoverage::kSingleChannel, color);
1506    }
1507
1508    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1509
1510private:
1511    void onPrepareDraws(Target* target) override {
1512        // Setup geometry processor
1513        sk_sp<GrGeometryProcessor> gp(
1514                new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
1515
1516        size_t vertexStride = gp->getVertexStride();
1517        SkASSERT(vertexStride == sizeof(DIEllipseVertex));
1518        QuadHelper helper;
1519        DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
1520                helper.init(target, vertexStride, fEllipses.count()));
1521        if (!verts) {
1522            return;
1523        }
1524
1525        for (const auto& ellipse : fEllipses) {
1526            GrColor color = ellipse.fColor;
1527            SkScalar xRadius = ellipse.fXRadius;
1528            SkScalar yRadius = ellipse.fYRadius;
1529
1530            const SkRect& bounds = ellipse.fBounds;
1531
1532            // This adjusts the "radius" to include the half-pixel border
1533            SkScalar offsetDx = ellipse.fGeoDx / xRadius;
1534            SkScalar offsetDy = ellipse.fGeoDy / yRadius;
1535
1536            SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
1537            SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
1538
1539            verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1540            verts[0].fColor = color;
1541            verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1542            verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1543
1544            verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1545            verts[1].fColor = color;
1546            verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1547            verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1548
1549            verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1550            verts[2].fColor = color;
1551            verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1552            verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1553
1554            verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1555            verts[3].fColor = color;
1556            verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1557            verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1558
1559            verts += kVerticesPerQuad;
1560        }
1561        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
1562    }
1563
1564    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1565        DIEllipseOp* that = t->cast<DIEllipseOp>();
1566        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1567            return false;
1568        }
1569
1570        if (this->style() != that->style()) {
1571            return false;
1572        }
1573
1574        // TODO rewrite to allow positioning on CPU
1575        if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1576            return false;
1577        }
1578
1579        fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
1580        this->joinBounds(*that);
1581        return true;
1582    }
1583
1584    const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
1585    DIEllipseStyle style() const { return fEllipses[0].fStyle; }
1586
1587    struct Ellipse {
1588        SkMatrix fViewMatrix;
1589        GrColor fColor;
1590        SkScalar fXRadius;
1591        SkScalar fYRadius;
1592        SkScalar fInnerXRadius;
1593        SkScalar fInnerYRadius;
1594        SkScalar fGeoDx;
1595        SkScalar fGeoDy;
1596        DIEllipseStyle fStyle;
1597        SkRect fBounds;
1598    };
1599
1600    Helper fHelper;
1601    SkSTArray<1, Ellipse, true> fEllipses;
1602
1603    typedef GrMeshDrawOp INHERITED;
1604};
1605
1606///////////////////////////////////////////////////////////////////////////////
1607
1608// We have three possible cases for geometry for a roundrect.
1609//
1610// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1611//    ____________
1612//   |_|________|_|
1613//   | |        | |
1614//   | |        | |
1615//   | |        | |
1616//   |_|________|_|
1617//   |_|________|_|
1618//
1619// For strokes, we don't draw the center quad.
1620//
1621// For circular roundrects, in the case where the stroke width is greater than twice
1622// the corner radius (overstroke), we add additional geometry to mark out the rectangle
1623// in the center. The shared vertices are duplicated so we can set a different outer radius
1624// for the fill calculation.
1625//    ____________
1626//   |_|________|_|
1627//   | |\ ____ /| |
1628//   | | |    | | |
1629//   | | |____| | |
1630//   |_|/______\|_|
1631//   |_|________|_|
1632//
1633// We don't draw the center quad from the fill rect in this case.
1634//
1635// For filled rrects that need to provide a distance vector we resuse the overstroke
1636// geometry but make the inner rect degenerate (either a point or a horizontal or
1637// vertical line).
1638
1639static const uint16_t gOverstrokeRRectIndices[] = {
1640        // clang-format off
1641        // overstroke quads
1642        // we place this at the beginning so that we can skip these indices when rendering normally
1643        16, 17, 19, 16, 19, 18,
1644        19, 17, 23, 19, 23, 21,
1645        21, 23, 22, 21, 22, 20,
1646        22, 16, 18, 22, 18, 20,
1647
1648        // corners
1649        0, 1, 5, 0, 5, 4,
1650        2, 3, 7, 2, 7, 6,
1651        8, 9, 13, 8, 13, 12,
1652        10, 11, 15, 10, 15, 14,
1653
1654        // edges
1655        1, 2, 6, 1, 6, 5,
1656        4, 5, 9, 4, 9, 8,
1657        6, 7, 11, 6, 11, 10,
1658        9, 10, 14, 9, 14, 13,
1659
1660        // center
1661        // we place this at the end so that we can ignore these indices when not rendering as filled
1662        5, 6, 10, 5, 10, 9,
1663        // clang-format on
1664};
1665
1666// fill and standard stroke indices skip the overstroke "ring"
1667static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
1668
1669// overstroke count is arraysize minus the center indices
1670static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1671// fill count skips overstroke indices and includes center
1672static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
1673// stroke count is fill count minus center indices
1674static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1675static const int kVertsPerStandardRRect = 16;
1676static const int kVertsPerOverstrokeRRect = 24;
1677
1678enum RRectType {
1679    kFill_RRectType,
1680    kStroke_RRectType,
1681    kOverstroke_RRectType,
1682};
1683
1684static int rrect_type_to_vert_count(RRectType type) {
1685    switch (type) {
1686        case kFill_RRectType:
1687        case kStroke_RRectType:
1688            return kVertsPerStandardRRect;
1689        case kOverstroke_RRectType:
1690            return kVertsPerOverstrokeRRect;
1691    }
1692    SK_ABORT("Invalid type");
1693    return 0;
1694}
1695
1696static int rrect_type_to_index_count(RRectType type) {
1697    switch (type) {
1698        case kFill_RRectType:
1699            return kIndicesPerFillRRect;
1700        case kStroke_RRectType:
1701            return kIndicesPerStrokeRRect;
1702        case kOverstroke_RRectType:
1703            return kIndicesPerOverstrokeRRect;
1704    }
1705    SK_ABORT("Invalid type");
1706    return 0;
1707}
1708
1709static const uint16_t* rrect_type_to_indices(RRectType type) {
1710    switch (type) {
1711        case kFill_RRectType:
1712        case kStroke_RRectType:
1713            return gStandardRRectIndices;
1714        case kOverstroke_RRectType:
1715            return gOverstrokeRRectIndices;
1716    }
1717    SK_ABORT("Invalid type");
1718    return 0;
1719}
1720
1721///////////////////////////////////////////////////////////////////////////////////////////////////
1722
1723// For distance computations in the interior of filled rrects we:
1724//
1725//   add a interior degenerate (point or line) rect
1726//   each vertex of that rect gets -outerRad as its radius
1727//      this makes the computation of the distance to the outer edge be negative
1728//      negative values are caught and then handled differently in the GP's onEmitCode
1729//   each vertex is also given the normalized x & y distance from the interior rect's edge
1730//      the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1731
1732class CircularRRectOp : public GrMeshDrawOp {
1733private:
1734    using Helper = GrSimpleMeshDrawOpHelper;
1735
1736public:
1737    DEFINE_OP_CLASS_ID
1738
1739    // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1740    // whether the rrect is only stroked or stroked and filled.
1741    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1742                                          const SkRect& devRect, float devRadius,
1743                                          float devStrokeWidth, bool strokeOnly) {
1744        return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect,
1745                                                      devRadius, devStrokeWidth, strokeOnly);
1746    }
1747    CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1748                    const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
1749            : INHERITED(ClassID())
1750            , fViewMatrixIfUsingLocalCoords(viewMatrix)
1751            , fHelper(helperArgs, GrAAType::kCoverage) {
1752        SkRect bounds = devRect;
1753        SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1754        SkScalar innerRadius = 0.0f;
1755        SkScalar outerRadius = devRadius;
1756        SkScalar halfWidth = 0;
1757        RRectType type = kFill_RRectType;
1758        if (devStrokeWidth > 0) {
1759            if (SkScalarNearlyZero(devStrokeWidth)) {
1760                halfWidth = SK_ScalarHalf;
1761            } else {
1762                halfWidth = SkScalarHalf(devStrokeWidth);
1763            }
1764
1765            if (strokeOnly) {
1766                // Outset stroke by 1/4 pixel
1767                devStrokeWidth += 0.25f;
1768                // If stroke is greater than width or height, this is still a fill
1769                // Otherwise we compute stroke params
1770                if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
1771                    innerRadius = devRadius - halfWidth;
1772                    type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
1773                }
1774            }
1775            outerRadius += halfWidth;
1776            bounds.outset(halfWidth, halfWidth);
1777        }
1778
1779        // The radii are outset for two reasons. First, it allows the shader to simply perform
1780        // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1781        // Second, the outer radius is used to compute the verts of the bounding box that is
1782        // rendered and the outset ensures the box will cover all partially covered by the rrect
1783        // corners.
1784        outerRadius += SK_ScalarHalf;
1785        innerRadius -= SK_ScalarHalf;
1786
1787        this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1788
1789        // Expand the rect for aa to generate correct vertices.
1790        bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1791
1792        fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
1793        fVertCount = rrect_type_to_vert_count(type);
1794        fIndexCount = rrect_type_to_index_count(type);
1795        fAllFill = (kFill_RRectType == type);
1796    }
1797
1798    const char* name() const override { return "CircularRRectOp"; }
1799
1800    void visitProxies(const VisitProxyFunc& func) const override {
1801        fHelper.visitProxies(func);
1802    }
1803
1804    SkString dumpInfo() const override {
1805        SkString string;
1806        for (int i = 0; i < fRRects.count(); ++i) {
1807            string.appendf(
1808                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1809                    "InnerRad: %.2f, OuterRad: %.2f\n",
1810                    fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
1811                    fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
1812                    fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
1813        }
1814        string += fHelper.dumpInfo();
1815        string += INHERITED::dumpInfo();
1816        return string;
1817    }
1818
1819    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1820                                GrPixelConfigIsClamped dstIsClamped) override {
1821        GrColor* color = &fRRects.front().fColor;
1822        return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1823                                            GrProcessorAnalysisCoverage::kSingleChannel, color);
1824    }
1825
1826    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1827
1828private:
1829    struct CircleVertex {
1830        SkPoint fPos;
1831        GrColor fColor;
1832        SkPoint fOffset;
1833        SkScalar fOuterRadius;
1834        SkScalar fInnerRadius;
1835        // No half plane, we don't use it here.
1836    };
1837
1838    static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
1839                                      SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
1840                                      SkScalar innerRadius, GrColor color) {
1841        SkASSERT(smInset < bigInset);
1842
1843        // TL
1844        (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1845        (*verts)->fColor = color;
1846        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1847        (*verts)->fOuterRadius = outerRadius;
1848        (*verts)->fInnerRadius = innerRadius;
1849        (*verts)++;
1850
1851        // TR
1852        (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
1853        (*verts)->fColor = color;
1854        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1855        (*verts)->fOuterRadius = outerRadius;
1856        (*verts)->fInnerRadius = innerRadius;
1857        (*verts)++;
1858
1859        (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
1860        (*verts)->fColor = color;
1861        (*verts)->fOffset = SkPoint::Make(0, 0);
1862        (*verts)->fOuterRadius = outerRadius;
1863        (*verts)->fInnerRadius = innerRadius;
1864        (*verts)++;
1865
1866        (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
1867        (*verts)->fColor = color;
1868        (*verts)->fOffset = SkPoint::Make(0, 0);
1869        (*verts)->fOuterRadius = outerRadius;
1870        (*verts)->fInnerRadius = innerRadius;
1871        (*verts)++;
1872
1873        (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1874        (*verts)->fColor = color;
1875        (*verts)->fOffset = SkPoint::Make(0, 0);
1876        (*verts)->fOuterRadius = outerRadius;
1877        (*verts)->fInnerRadius = innerRadius;
1878        (*verts)++;
1879
1880        (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1881        (*verts)->fColor = color;
1882        (*verts)->fOffset = SkPoint::Make(0, 0);
1883        (*verts)->fOuterRadius = outerRadius;
1884        (*verts)->fInnerRadius = innerRadius;
1885        (*verts)++;
1886
1887        // BL
1888        (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1889        (*verts)->fColor = color;
1890        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1891        (*verts)->fOuterRadius = outerRadius;
1892        (*verts)->fInnerRadius = innerRadius;
1893        (*verts)++;
1894
1895        // BR
1896        (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1897        (*verts)->fColor = color;
1898        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1899        (*verts)->fOuterRadius = outerRadius;
1900        (*verts)->fInnerRadius = innerRadius;
1901        (*verts)++;
1902    }
1903
1904    void onPrepareDraws(Target* target) override {
1905        // Invert the view matrix as a local matrix (if any other processors require coords).
1906        SkMatrix localMatrix;
1907        if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1908            return;
1909        }
1910
1911        // Setup geometry processor
1912        sk_sp<GrGeometryProcessor> gp(
1913                new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
1914
1915        size_t vertexStride = gp->getVertexStride();
1916        SkASSERT(sizeof(CircleVertex) == vertexStride);
1917
1918        const GrBuffer* vertexBuffer;
1919        int firstVertex;
1920
1921        CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
1922                                                                     &vertexBuffer, &firstVertex);
1923        if (!verts) {
1924            SkDebugf("Could not allocate vertices\n");
1925            return;
1926        }
1927
1928        const GrBuffer* indexBuffer = nullptr;
1929        int firstIndex = 0;
1930        uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1931        if (!indices) {
1932            SkDebugf("Could not allocate indices\n");
1933            return;
1934        }
1935
1936        int currStartVertex = 0;
1937        for (const auto& rrect : fRRects) {
1938            GrColor color = rrect.fColor;
1939            SkScalar outerRadius = rrect.fOuterRadius;
1940            const SkRect& bounds = rrect.fDevBounds;
1941
1942            SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
1943                                   bounds.fBottom - outerRadius, bounds.fBottom};
1944
1945            SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
1946            // The inner radius in the vertex data must be specified in normalized space.
1947            // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
1948            SkScalar innerRadius = rrect.fType != kFill_RRectType
1949                                           ? rrect.fInnerRadius / rrect.fOuterRadius
1950                                           : -1.0f / rrect.fOuterRadius;
1951            for (int i = 0; i < 4; ++i) {
1952                verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1953                verts->fColor = color;
1954                verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1955                verts->fOuterRadius = outerRadius;
1956                verts->fInnerRadius = innerRadius;
1957                verts++;
1958
1959                verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1960                verts->fColor = color;
1961                verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1962                verts->fOuterRadius = outerRadius;
1963                verts->fInnerRadius = innerRadius;
1964                verts++;
1965
1966                verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1967                verts->fColor = color;
1968                verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1969                verts->fOuterRadius = outerRadius;
1970                verts->fInnerRadius = innerRadius;
1971                verts++;
1972
1973                verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1974                verts->fColor = color;
1975                verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1976                verts->fOuterRadius = outerRadius;
1977                verts->fInnerRadius = innerRadius;
1978                verts++;
1979            }
1980            // Add the additional vertices for overstroked rrects.
1981            // Effectively this is an additional stroked rrect, with its
1982            // outer radius = outerRadius - innerRadius, and inner radius = 0.
1983            // This will give us correct AA in the center and the correct
1984            // distance to the outer edge.
1985            //
1986            // Also, the outer offset is a constant vector pointing to the right, which
1987            // guarantees that the distance value along the outer rectangle is constant.
1988            if (kOverstroke_RRectType == rrect.fType) {
1989                SkASSERT(rrect.fInnerRadius <= 0.0f);
1990
1991                SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
1992                // this is the normalized distance from the outer rectangle of this
1993                // geometry to the outer edge
1994                SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
1995
1996                FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
1997                                      overstrokeOuterRadius, 0.0f, rrect.fColor);
1998            }
1999
2000            const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2001            const int primIndexCount = rrect_type_to_index_count(rrect.fType);
2002            for (int i = 0; i < primIndexCount; ++i) {
2003                *indices++ = primIndices[i] + currStartVertex;
2004            }
2005
2006            currStartVertex += rrect_type_to_vert_count(rrect.fType);
2007        }
2008
2009        GrMesh mesh(GrPrimitiveType::kTriangles);
2010        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
2011        mesh.setVertexData(vertexBuffer, firstVertex);
2012        target->draw(gp.get(), fHelper.makePipeline(target), mesh);
2013    }
2014
2015    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
2016        CircularRRectOp* that = t->cast<CircularRRectOp>();
2017
2018        // can only represent 65535 unique vertices with 16-bit indices
2019        if (fVertCount + that->fVertCount > 65536) {
2020            return false;
2021        }
2022
2023        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
2024            return false;
2025        }
2026
2027        if (fHelper.usesLocalCoords() &&
2028            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
2029            return false;
2030        }
2031
2032        fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
2033        this->joinBounds(*that);
2034        fVertCount += that->fVertCount;
2035        fIndexCount += that->fIndexCount;
2036        fAllFill = fAllFill && that->fAllFill;
2037        return true;
2038    }
2039
2040    struct RRect {
2041        GrColor fColor;
2042        SkScalar fInnerRadius;
2043        SkScalar fOuterRadius;
2044        SkRect fDevBounds;
2045        RRectType fType;
2046    };
2047
2048    SkMatrix fViewMatrixIfUsingLocalCoords;
2049    Helper fHelper;
2050    int fVertCount;
2051    int fIndexCount;
2052    bool fAllFill;
2053    SkSTArray<1, RRect, true> fRRects;
2054
2055    typedef GrMeshDrawOp INHERITED;
2056};
2057
2058static const int kNumRRectsInIndexBuffer = 256;
2059
2060GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2061GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2062static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2063                                                    GrResourceProvider* resourceProvider) {
2064    GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2065    GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2066    switch (type) {
2067        case kFill_RRectType:
2068            return resourceProvider->findOrCreatePatternedIndexBuffer(
2069                    gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2070                    kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
2071        case kStroke_RRectType:
2072            return resourceProvider->findOrCreatePatternedIndexBuffer(
2073                    gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2074                    kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
2075        default:
2076            SkASSERT(false);
2077            return nullptr;
2078    };
2079}
2080
2081class EllipticalRRectOp : public GrMeshDrawOp {
2082private:
2083    using Helper = GrSimpleMeshDrawOpHelper;
2084
2085public:
2086    DEFINE_OP_CLASS_ID
2087
2088    // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2089    // whether the rrect is only stroked or stroked and filled.
2090    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
2091                                          const SkRect& devRect, float devXRadius, float devYRadius,
2092                                          SkVector devStrokeWidths, bool strokeOnly) {
2093        SkASSERT(devXRadius > 0.5);
2094        SkASSERT(devYRadius > 0.5);
2095        SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2096        SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
2097        if (devStrokeWidths.fX > 0) {
2098            if (SkScalarNearlyZero(devStrokeWidths.length())) {
2099                devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2100            } else {
2101                devStrokeWidths.scale(SK_ScalarHalf);
2102            }
2103
2104            // we only handle thick strokes for near-circular ellipses
2105            if (devStrokeWidths.length() > SK_ScalarHalf &&
2106                (SK_ScalarHalf * devXRadius > devYRadius ||
2107                 SK_ScalarHalf * devYRadius > devXRadius)) {
2108                return nullptr;
2109            }
2110
2111            // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2112            if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2113                (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
2114                return nullptr;
2115            }
2116            if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2117                (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
2118                return nullptr;
2119            }
2120        }
2121        return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect,
2122                                                        devXRadius, devYRadius, devStrokeWidths,
2123                                                        strokeOnly);
2124    }
2125
2126    EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
2127                      const SkRect& devRect, float devXRadius, float devYRadius,
2128                      SkVector devStrokeHalfWidths, bool strokeOnly)
2129            : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
2130        SkScalar innerXRadius = 0.0f;
2131        SkScalar innerYRadius = 0.0f;
2132        SkRect bounds = devRect;
2133        bool stroked = false;
2134        if (devStrokeHalfWidths.fX > 0) {
2135            // this is legit only if scale & translation (which should be the case at the moment)
2136            if (strokeOnly) {
2137                innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2138                innerYRadius = devYRadius - devStrokeHalfWidths.fY;
2139                stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2140            }
2141
2142            devXRadius += devStrokeHalfWidths.fX;
2143            devYRadius += devStrokeHalfWidths.fY;
2144            bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
2145        }
2146
2147        fStroked = stroked;
2148        fViewMatrixIfUsingLocalCoords = viewMatrix;
2149        this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2150        // Expand the rect for aa in order to generate the correct vertices.
2151        bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2152        fRRects.emplace_back(
2153                RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
2154    }
2155
2156    const char* name() const override { return "EllipticalRRectOp"; }
2157
2158    void visitProxies(const VisitProxyFunc& func) const override {
2159        fHelper.visitProxies(func);
2160    }
2161
2162    SkString dumpInfo() const override {
2163        SkString string;
2164        string.appendf("Stroked: %d\n", fStroked);
2165        for (const auto& geo : fRRects) {
2166            string.appendf(
2167                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2168                    "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2169                    geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2170                    geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2171                    geo.fInnerYRadius);
2172        }
2173        string += fHelper.dumpInfo();
2174        string += INHERITED::dumpInfo();
2175        return string;
2176    }
2177
2178    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2179                                GrPixelConfigIsClamped dstIsClamped) override {
2180        GrColor* color = &fRRects.front().fColor;
2181        return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2182                                            GrProcessorAnalysisCoverage::kSingleChannel, color);
2183    }
2184
2185    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2186
2187private:
2188    void onPrepareDraws(Target* target) override {
2189        SkMatrix localMatrix;
2190        if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
2191            return;
2192        }
2193
2194        // Setup geometry processor
2195        sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
2196
2197        size_t vertexStride = gp->getVertexStride();
2198        SkASSERT(vertexStride == sizeof(EllipseVertex));
2199
2200        // drop out the middle quad if we're stroked
2201        int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
2202        sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2203                fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
2204
2205        PatternHelper helper(GrPrimitiveType::kTriangles);
2206        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
2207                helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect,
2208                            indicesPerInstance, fRRects.count()));
2209        if (!verts || !indexBuffer) {
2210            SkDebugf("Could not allocate vertices\n");
2211            return;
2212        }
2213
2214        for (const auto& rrect : fRRects) {
2215            GrColor color = rrect.fColor;
2216            // Compute the reciprocals of the radii here to save time in the shader
2217            SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
2218            SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
2219            SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
2220            SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
2221
2222            // Extend the radii out half a pixel to antialias.
2223            SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2224            SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
2225
2226            const SkRect& bounds = rrect.fDevBounds;
2227
2228            SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2229                                   bounds.fBottom - yOuterRadius, bounds.fBottom};
2230            SkScalar yOuterOffsets[4] = {yOuterRadius,
2231                                         SK_ScalarNearlyZero,  // we're using inversesqrt() in
2232                                                               // shader, so can't be exactly 0
2233                                         SK_ScalarNearlyZero, yOuterRadius};
2234
2235            for (int i = 0; i < 4; ++i) {
2236                verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
2237                verts->fColor = color;
2238                verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2239                verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2240                verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2241                verts++;
2242
2243                verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
2244                verts->fColor = color;
2245                verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2246                verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2247                verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2248                verts++;
2249
2250                verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
2251                verts->fColor = color;
2252                verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2253                verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2254                verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2255                verts++;
2256
2257                verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
2258                verts->fColor = color;
2259                verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2260                verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2261                verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2262                verts++;
2263            }
2264        }
2265        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
2266    }
2267
2268    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
2269        EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
2270
2271        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
2272            return false;
2273        }
2274
2275        if (fStroked != that->fStroked) {
2276            return false;
2277        }
2278
2279        if (fHelper.usesLocalCoords() &&
2280            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
2281            return false;
2282        }
2283
2284        fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
2285        this->joinBounds(*that);
2286        return true;
2287    }
2288
2289    struct RRect {
2290        GrColor fColor;
2291        SkScalar fXRadius;
2292        SkScalar fYRadius;
2293        SkScalar fInnerXRadius;
2294        SkScalar fInnerYRadius;
2295        SkRect fDevBounds;
2296    };
2297
2298    SkMatrix fViewMatrixIfUsingLocalCoords;
2299    Helper fHelper;
2300    bool fStroked;
2301    SkSTArray<1, RRect, true> fRRects;
2302
2303    typedef GrMeshDrawOp INHERITED;
2304};
2305
2306static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint,
2307                                               const SkMatrix& viewMatrix,
2308                                               const SkRRect& rrect,
2309                                               const SkStrokeRec& stroke) {
2310    SkASSERT(viewMatrix.rectStaysRect());
2311    SkASSERT(rrect.isSimple());
2312    SkASSERT(!rrect.isOval());
2313
2314    // RRect ops only handle simple, but not too simple, rrects.
2315    // Do any matrix crunching before we reset the draw state for device coords.
2316    const SkRect& rrectBounds = rrect.getBounds();
2317    SkRect bounds;
2318    viewMatrix.mapRect(&bounds, rrectBounds);
2319
2320    SkVector radii = rrect.getSimpleRadii();
2321    SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2322                                   viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2323    SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2324                                   viewMatrix[SkMatrix::kMScaleY] * radii.fY);
2325
2326    SkStrokeRec::Style style = stroke.getStyle();
2327
2328    // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2329    SkVector scaledStroke = {-1, -1};
2330    SkScalar strokeWidth = stroke.getWidth();
2331
2332    bool isStrokeOnly =
2333            SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
2334    bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2335
2336    bool isCircular = (xRadius == yRadius);
2337    if (hasStroke) {
2338        if (SkStrokeRec::kHairline_Style == style) {
2339            scaledStroke.set(1, 1);
2340        } else {
2341            scaledStroke.fX = SkScalarAbs(
2342                    strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2343            scaledStroke.fY = SkScalarAbs(
2344                    strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
2345        }
2346
2347        isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2348        // for non-circular rrects, if half of strokewidth is greater than radius,
2349        // we don't handle that right now
2350        if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2351                            SK_ScalarHalf * scaledStroke.fY > yRadius)) {
2352            return nullptr;
2353        }
2354    }
2355
2356    // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2357    // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2358    // patch will have fractional coverage. This only matters when the interior is actually filled.
2359    // We could consider falling back to rect rendering here, since a tiny radius is
2360    // indistinguishable from a square corner.
2361    if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
2362        return nullptr;
2363    }
2364
2365    // if the corners are circles, use the circle renderer
2366    if (isCircular) {
2367        return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX,
2368                                     isStrokeOnly);
2369        // otherwise we use the ellipse renderer
2370    } else {
2371        return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius,
2372                                       scaledStroke, isStrokeOnly);
2373    }
2374}
2375
2376std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint,
2377                                                       const SkMatrix& viewMatrix,
2378                                                       const SkRRect& rrect,
2379                                                       const SkStrokeRec& stroke,
2380                                                       const GrShaderCaps* shaderCaps) {
2381    if (rrect.isOval()) {
2382        return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), stroke, shaderCaps);
2383    }
2384
2385    if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2386        return nullptr;
2387    }
2388
2389    return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke);
2390}
2391
2392///////////////////////////////////////////////////////////////////////////////
2393
2394std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint,
2395                                                      const SkMatrix& viewMatrix,
2396                                                      const SkRect& oval,
2397                                                      const SkStrokeRec& stroke,
2398                                                      const GrShaderCaps* shaderCaps) {
2399    // we can draw circles
2400    SkScalar width = oval.width();
2401    if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
2402        circle_stays_circle(viewMatrix)) {
2403        SkPoint center = {oval.centerX(), oval.centerY()};
2404        return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f,
2405                              GrStyle(stroke, nullptr));
2406    }
2407
2408    // prefer the device space ellipse op for batchability
2409    if (viewMatrix.rectStaysRect()) {
2410        return EllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
2411    }
2412
2413    // Otherwise, if we have shader derivative support, render as device-independent
2414    if (shaderCaps->shaderDerivativeSupport()) {
2415        return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
2416    }
2417
2418    return nullptr;
2419}
2420
2421///////////////////////////////////////////////////////////////////////////////
2422
2423std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix,
2424                                                     const SkRect& oval, SkScalar startAngle,
2425                                                     SkScalar sweepAngle, bool useCenter,
2426                                                     const GrStyle& style,
2427                                                     const GrShaderCaps* shaderCaps) {
2428    SkASSERT(!oval.isEmpty());
2429    SkASSERT(sweepAngle);
2430    SkScalar width = oval.width();
2431    if (SkScalarAbs(sweepAngle) >= 360.f) {
2432        return nullptr;
2433    }
2434    if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2435        return nullptr;
2436    }
2437    SkPoint center = {oval.centerX(), oval.centerY()};
2438    CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2439                                     useCenter};
2440    return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams);
2441}
2442
2443///////////////////////////////////////////////////////////////////////////////
2444
2445#if GR_TEST_UTILS
2446
2447GR_DRAW_OP_TEST_DEFINE(CircleOp) {
2448    do {
2449        SkScalar rotate = random->nextSScalar1() * 360.f;
2450        SkScalar translateX = random->nextSScalar1() * 1000.f;
2451        SkScalar translateY = random->nextSScalar1() * 1000.f;
2452        SkScalar scale = random->nextSScalar1() * 100.f;
2453        SkMatrix viewMatrix;
2454        viewMatrix.setRotate(rotate);
2455        viewMatrix.postTranslate(translateX, translateY);
2456        viewMatrix.postScale(scale, scale);
2457        SkRect circle = GrTest::TestSquare(random);
2458        SkPoint center = {circle.centerX(), circle.centerY()};
2459        SkScalar radius = circle.width() / 2.f;
2460        SkStrokeRec stroke = GrTest::TestStrokeRec(random);
2461        CircleOp::ArcParams arcParamsTmp;
2462        const CircleOp::ArcParams* arcParams = nullptr;
2463        if (random->nextBool()) {
2464            arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
2465            arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2466            arcParamsTmp.fUseCenter = random->nextBool();
2467            arcParams = &arcParamsTmp;
2468        }
2469        std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius,
2470                                                      GrStyle(stroke, nullptr), arcParams);
2471        if (op) {
2472            return op;
2473        }
2474    } while (true);
2475}
2476
2477GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
2478    SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2479    SkRect ellipse = GrTest::TestSquare(random);
2480    return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
2481}
2482
2483GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
2484    SkMatrix viewMatrix = GrTest::TestMatrix(random);
2485    SkRect ellipse = GrTest::TestSquare(random);
2486    return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
2487}
2488
2489GR_DRAW_OP_TEST_DEFINE(RRectOp) {
2490    SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2491    const SkRRect& rrect = GrTest::TestRRectSimple(random);
2492    return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random));
2493}
2494
2495#endif
2496