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