LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/gpu/ops - GrOvalOpFactory.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1263 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 113 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13