Home | History | Annotate | Download | only in gpu
      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 "GrOvalRenderer.h"
      9 
     10 #include "GrBatchFlushState.h"
     11 #include "GrBatchTest.h"
     12 #include "GrGeometryProcessor.h"
     13 #include "GrInvariantOutput.h"
     14 #include "GrProcessor.h"
     15 #include "GrResourceProvider.h"
     16 #include "SkRRect.h"
     17 #include "SkStrokeRec.h"
     18 #include "batches/GrVertexBatch.h"
     19 #include "glsl/GrGLSLFragmentShaderBuilder.h"
     20 #include "glsl/GrGLSLGeometryProcessor.h"
     21 #include "glsl/GrGLSLProgramDataManager.h"
     22 #include "glsl/GrGLSLVarying.h"
     23 #include "glsl/GrGLSLVertexShaderBuilder.h"
     24 #include "glsl/GrGLSLUniformHandler.h"
     25 #include "glsl/GrGLSLUtil.h"
     26 
     27 // TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
     28 
     29 namespace {
     30 
     31 struct CircleVertex {
     32     SkPoint  fPos;
     33     GrColor  fColor;
     34     SkPoint  fOffset;
     35     SkScalar fOuterRadius;
     36     SkScalar fInnerRadius;
     37 };
     38 
     39 struct EllipseVertex {
     40     SkPoint  fPos;
     41     GrColor  fColor;
     42     SkPoint  fOffset;
     43     SkPoint  fOuterRadii;
     44     SkPoint  fInnerRadii;
     45 };
     46 
     47 struct DIEllipseVertex {
     48     SkPoint  fPos;
     49     GrColor  fColor;
     50     SkPoint  fOuterOffset;
     51     SkPoint  fInnerOffset;
     52 };
     53 
     54 inline bool circle_stays_circle(const SkMatrix& m) {
     55     return m.isSimilarity();
     56 }
     57 
     58 }
     59 
     60 ///////////////////////////////////////////////////////////////////////////////
     61 
     62 /**
     63  * The output of this effect is a modulation of the input color and coverage for a circle. It
     64  * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
     65  * with origin at the circle center. Two   vertex attributes are used:
     66  *    vec2f : position in device space of the bounding geometry vertices
     67  *    vec4f : (p.xy, outerRad, innerRad)
     68  *             p is the position in the normalized space.
     69  *             outerRad is the outerRadius in device space.
     70  *             innerRad is the innerRadius in normalized space (ignored if not stroking).
     71  */
     72 
     73 class CircleEdgeEffect : public GrGeometryProcessor {
     74 public:
     75     static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
     76                                        bool usesLocalCoords) {
     77         return new CircleEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
     78     }
     79 
     80     const Attribute* inPosition() const { return fInPosition; }
     81     const Attribute* inColor() const { return fInColor; }
     82     const Attribute* inCircleEdge() const { return fInCircleEdge; }
     83     GrColor color() const { return fColor; }
     84     bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
     85     const SkMatrix& localMatrix() const { return fLocalMatrix; }
     86     bool usesLocalCoords() const { return fUsesLocalCoords; }
     87     virtual ~CircleEdgeEffect() {}
     88 
     89     const char* name() const override { return "CircleEdge"; }
     90 
     91     inline bool isStroked() const { return fStroke; }
     92 
     93     class GLSLProcessor : public GrGLSLGeometryProcessor {
     94     public:
     95         GLSLProcessor() {}
     96 
     97         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
     98             const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
     99             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
    100             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
    101             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
    102 
    103             // emit attributes
    104             varyingHandler->emitAttributes(ce);
    105 
    106             GrGLSLVertToFrag v(kVec4f_GrSLType);
    107             varyingHandler->addVarying("CircleEdge", &v);
    108             vertBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
    109 
    110             GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
    111             // setup pass through color
    112             if (!ce.colorIgnored()) {
    113                 varyingHandler->addPassThroughAttribute(ce.inColor(), args.fOutputColor);
    114             }
    115 
    116             // Setup position
    117             this->setupPosition(vertBuilder, gpArgs, ce.inPosition()->fName);
    118 
    119             // emit transforms
    120             this->emitTransforms(vertBuilder,
    121                                  varyingHandler,
    122                                  uniformHandler,
    123                                  gpArgs->fPositionVar,
    124                                  ce.inPosition()->fName,
    125                                  ce.localMatrix(),
    126                                  args.fTransformsIn,
    127                                  args.fTransformsOut);
    128 
    129             fragBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
    130             fragBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);",
    131                                      v.fsIn());
    132             if (ce.isStroked()) {
    133                 fragBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
    134                                          v.fsIn(), v.fsIn());
    135                 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
    136             }
    137 
    138             fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
    139         }
    140 
    141         static void GenKey(const GrGeometryProcessor& gp,
    142                            const GrGLSLCaps&,
    143                            GrProcessorKeyBuilder* b) {
    144             const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
    145             uint16_t key = ce.isStroked() ? 0x1 : 0x0;
    146             key |= ce.usesLocalCoords() && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
    147             key |= ce.colorIgnored() ? 0x4 : 0x0;
    148             b->add32(key);
    149         }
    150 
    151         void setData(const GrGLSLProgramDataManager& pdman,
    152                      const GrPrimitiveProcessor& gp) override {
    153         }
    154 
    155         void setTransformData(const GrPrimitiveProcessor& primProc,
    156                               const GrGLSLProgramDataManager& pdman,
    157                               int index,
    158                               const SkTArray<const GrCoordTransform*, true>& transforms) override {
    159             this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
    160         }
    161 
    162     private:
    163         typedef GrGLSLGeometryProcessor INHERITED;
    164     };
    165 
    166     void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
    167         GLSLProcessor::GenKey(*this, caps, b);
    168     }
    169 
    170     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
    171         return new GLSLProcessor();
    172     }
    173 
    174 private:
    175     CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix, bool usesLocalCoords)
    176         : fColor(color)
    177         , fLocalMatrix(localMatrix)
    178         , fUsesLocalCoords(usesLocalCoords) {
    179         this->initClassID<CircleEdgeEffect>();
    180         fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
    181                                                        kHigh_GrSLPrecision));
    182         fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
    183         fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
    184                                                            kVec4f_GrVertexAttribType));
    185         fStroke = stroke;
    186     }
    187 
    188     GrColor fColor;
    189     SkMatrix fLocalMatrix;
    190     const Attribute* fInPosition;
    191     const Attribute* fInColor;
    192     const Attribute* fInCircleEdge;
    193     bool fStroke;
    194     bool fUsesLocalCoords;
    195 
    196     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
    197 
    198     typedef GrGeometryProcessor INHERITED;
    199 };
    200 
    201 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
    202 
    203 const GrGeometryProcessor* CircleEdgeEffect::TestCreate(GrProcessorTestData* d) {
    204     return CircleEdgeEffect::Create(GrRandomColor(d->fRandom),
    205                                     d->fRandom->nextBool(),
    206                                     GrTest::TestMatrix(d->fRandom),
    207                                     d->fRandom->nextBool());
    208 }
    209 
    210 ///////////////////////////////////////////////////////////////////////////////
    211 
    212 /**
    213  * The output of this effect is a modulation of the input color and coverage for an axis-aligned
    214  * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
    215  * in both x and y directions.
    216  *
    217  * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
    218  */
    219 
    220 class EllipseEdgeEffect : public GrGeometryProcessor {
    221 public:
    222     static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
    223                                        bool usesLocalCoords) {
    224         return new EllipseEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
    225     }
    226 
    227     virtual ~EllipseEdgeEffect() {}
    228 
    229     const char* name() const override { return "EllipseEdge"; }
    230 
    231     const Attribute* inPosition() const { return fInPosition; }
    232     const Attribute* inColor() const { return fInColor; }
    233     const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
    234     const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
    235     GrColor color() const { return fColor; }
    236     bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
    237     const SkMatrix& localMatrix() const { return fLocalMatrix; }
    238     bool usesLocalCoords() const { return fUsesLocalCoords; }
    239 
    240     inline bool isStroked() const { return fStroke; }
    241 
    242     class GLSLProcessor : public GrGLSLGeometryProcessor {
    243     public:
    244         GLSLProcessor() {}
    245 
    246         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
    247             const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
    248             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
    249             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
    250             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
    251 
    252             // emit attributes
    253             varyingHandler->emitAttributes(ee);
    254 
    255             GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
    256             varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
    257             vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
    258                                    ee.inEllipseOffset()->fName);
    259 
    260             GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
    261             varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
    262             vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
    263                                    ee.inEllipseRadii()->fName);
    264 
    265             GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
    266             // setup pass through color
    267             if (!ee.colorIgnored()) {
    268                 varyingHandler->addPassThroughAttribute(ee.inColor(), args.fOutputColor);
    269             }
    270 
    271             // Setup position
    272             this->setupPosition(vertBuilder, gpArgs, ee.inPosition()->fName);
    273 
    274             // emit transforms
    275             this->emitTransforms(vertBuilder,
    276                                  varyingHandler,
    277                                  uniformHandler,
    278                                  gpArgs->fPositionVar,
    279                                  ee.inPosition()->fName,
    280                                  ee.localMatrix(),
    281                                  args.fTransformsIn,
    282                                  args.fTransformsOut);
    283 
    284             // for outer curve
    285             fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
    286                                      ellipseRadii.fsIn());
    287             fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
    288             fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
    289             fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
    290 
    291             // avoid calling inversesqrt on zero.
    292             fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
    293             fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
    294             fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
    295 
    296             // for inner curve
    297             if (ee.isStroked()) {
    298                 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
    299                                          ellipseOffsets.fsIn(), ellipseRadii.fsIn());
    300                 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
    301                 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
    302                                          ellipseRadii.fsIn());
    303                 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
    304                 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
    305             }
    306 
    307             fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
    308         }
    309 
    310         static void GenKey(const GrGeometryProcessor& gp,
    311                            const GrGLSLCaps&,
    312                            GrProcessorKeyBuilder* b) {
    313             const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
    314             uint16_t key = ee.isStroked() ? 0x1 : 0x0;
    315             key |= ee.usesLocalCoords() && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
    316             key |= ee.colorIgnored() ? 0x4 : 0x0;
    317             b->add32(key);
    318         }
    319 
    320         void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
    321         }
    322 
    323         void setTransformData(const GrPrimitiveProcessor& primProc,
    324                               const GrGLSLProgramDataManager& pdman,
    325                               int index,
    326                               const SkTArray<const GrCoordTransform*, true>& transforms) override {
    327             this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
    328         }
    329 
    330     private:
    331         typedef GrGLSLGeometryProcessor INHERITED;
    332     };
    333 
    334     void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
    335         GLSLProcessor::GenKey(*this, caps, b);
    336     }
    337 
    338     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
    339         return new GLSLProcessor();
    340     }
    341 
    342 private:
    343     EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix,
    344                       bool usesLocalCoords)
    345         : fColor(color)
    346         , fLocalMatrix(localMatrix)
    347         , fUsesLocalCoords(usesLocalCoords) {
    348         this->initClassID<EllipseEdgeEffect>();
    349         fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
    350         fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
    351         fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
    352                                                             kVec2f_GrVertexAttribType));
    353         fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
    354                                                            kVec4f_GrVertexAttribType));
    355         fStroke = stroke;
    356     }
    357 
    358     const Attribute* fInPosition;
    359     const Attribute* fInColor;
    360     const Attribute* fInEllipseOffset;
    361     const Attribute* fInEllipseRadii;
    362     GrColor fColor;
    363     SkMatrix fLocalMatrix;
    364     bool fStroke;
    365     bool fUsesLocalCoords;
    366 
    367     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
    368 
    369     typedef GrGeometryProcessor INHERITED;
    370 };
    371 
    372 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
    373 
    374 const GrGeometryProcessor* EllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
    375     return EllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
    376                                      d->fRandom->nextBool(),
    377                                      GrTest::TestMatrix(d->fRandom),
    378                                      d->fRandom->nextBool());
    379 }
    380 
    381 ///////////////////////////////////////////////////////////////////////////////
    382 
    383 /**
    384  * The output of this effect is a modulation of the input color and coverage for an ellipse,
    385  * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
    386  * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
    387  * using differentials.
    388  *
    389  * The result is device-independent and can be used with any affine matrix.
    390  */
    391 
    392 class DIEllipseEdgeEffect : public GrGeometryProcessor {
    393 public:
    394     enum Mode { kStroke = 0, kHairline, kFill };
    395 
    396     static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode,
    397                                        bool usesLocalCoords) {
    398         return new DIEllipseEdgeEffect(color, viewMatrix, mode, usesLocalCoords);
    399     }
    400 
    401     virtual ~DIEllipseEdgeEffect() {}
    402 
    403     const char* name() const override { return "DIEllipseEdge"; }
    404 
    405     const Attribute* inPosition() const { return fInPosition; }
    406     const Attribute* inColor() const { return fInColor; }
    407     const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
    408     const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
    409     GrColor color() const { return fColor; }
    410     bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
    411     const SkMatrix& viewMatrix() const { return fViewMatrix; }
    412     bool usesLocalCoords() const { return fUsesLocalCoords; }
    413 
    414     inline Mode getMode() const { return fMode; }
    415 
    416     class GLSLProcessor : public GrGLSLGeometryProcessor {
    417     public:
    418         GLSLProcessor()
    419             : fViewMatrix(SkMatrix::InvalidMatrix()) {}
    420 
    421         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
    422             const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
    423             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
    424             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
    425             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
    426 
    427             // emit attributes
    428             varyingHandler->emitAttributes(ee);
    429 
    430             GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
    431             varyingHandler->addVarying("EllipseOffsets0", &offsets0);
    432             vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
    433                                    ee.inEllipseOffsets0()->fName);
    434 
    435             GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
    436             varyingHandler->addVarying("EllipseOffsets1", &offsets1);
    437             vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
    438                                    ee.inEllipseOffsets1()->fName);
    439 
    440             GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
    441             // setup pass through color
    442             if (!ee.colorIgnored()) {
    443                 varyingHandler->addPassThroughAttribute(ee.inColor(), args.fOutputColor);
    444             }
    445 
    446             // Setup position
    447             this->setupPosition(vertBuilder,
    448                                 uniformHandler,
    449                                 gpArgs,
    450                                 ee.inPosition()->fName,
    451                                 ee.viewMatrix(),
    452                                 &fViewMatrixUniform);
    453 
    454             // emit transforms
    455             this->emitTransforms(vertBuilder,
    456                                  varyingHandler,
    457                                  uniformHandler,
    458                                  gpArgs->fPositionVar,
    459                                  ee.inPosition()->fName,
    460                                  args.fTransformsIn,
    461                                  args.fTransformsOut);
    462 
    463             SkAssertResult(fragBuilder->enableFeature(
    464                     GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
    465             // for outer curve
    466             fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
    467             fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
    468             fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
    469             fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
    470             fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
    471                                      "                 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
    472                                      offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
    473 
    474             fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
    475             // avoid calling inversesqrt on zero.
    476             fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
    477             fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
    478             if (kHairline == ee.getMode()) {
    479                 // can probably do this with one step
    480                 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
    481                 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
    482             } else {
    483                 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
    484             }
    485 
    486             // for inner curve
    487             if (kStroke == ee.getMode()) {
    488                 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
    489                 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
    490                 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
    491                 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
    492                 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
    493                                          "            2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
    494                                          offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
    495                                          offsets1.fsIn());
    496                 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
    497                 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
    498             }
    499 
    500             fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
    501         }
    502 
    503         static void GenKey(const GrGeometryProcessor& gp,
    504                            const GrGLSLCaps&,
    505                            GrProcessorKeyBuilder* b) {
    506             const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
    507             uint16_t key = ellipseEffect.getMode();
    508             key |= ellipseEffect.colorIgnored() << 9;
    509             key |= ComputePosKey(ellipseEffect.viewMatrix()) << 10;
    510             b->add32(key);
    511         }
    512 
    513         void setData(const GrGLSLProgramDataManager& pdman,
    514                      const GrPrimitiveProcessor& gp) override {
    515             const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
    516 
    517             if (!dee.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dee.viewMatrix())) {
    518                 fViewMatrix = dee.viewMatrix();
    519                 float viewMatrix[3 * 3];
    520                 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
    521                 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
    522             }
    523         }
    524 
    525     private:
    526         SkMatrix fViewMatrix;
    527         UniformHandle fViewMatrixUniform;
    528 
    529         typedef GrGLSLGeometryProcessor INHERITED;
    530     };
    531 
    532     void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
    533         GLSLProcessor::GenKey(*this, caps, b);
    534     }
    535 
    536     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
    537         return new GLSLProcessor();
    538     }
    539 
    540 private:
    541     DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode,
    542                         bool usesLocalCoords)
    543         : fColor(color)
    544         , fViewMatrix(viewMatrix)
    545         , fUsesLocalCoords(usesLocalCoords) {
    546         this->initClassID<DIEllipseEdgeEffect>();
    547         fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
    548                                                        kHigh_GrSLPrecision));
    549         fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
    550         fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
    551                                                               kVec2f_GrVertexAttribType));
    552         fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
    553                                                               kVec2f_GrVertexAttribType));
    554         fMode = mode;
    555     }
    556 
    557     const Attribute* fInPosition;
    558     const Attribute* fInColor;
    559     const Attribute* fInEllipseOffsets0;
    560     const Attribute* fInEllipseOffsets1;
    561     GrColor fColor;
    562     SkMatrix fViewMatrix;
    563     Mode fMode;
    564     bool fUsesLocalCoords;
    565 
    566     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
    567 
    568     typedef GrGeometryProcessor INHERITED;
    569 };
    570 
    571 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
    572 
    573 const GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
    574     return DIEllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
    575                                        GrTest::TestMatrix(d->fRandom),
    576                                        (Mode)(d->fRandom->nextRangeU(0,2)),
    577                                        d->fRandom->nextBool());
    578 }
    579 
    580 ///////////////////////////////////////////////////////////////////////////////
    581 
    582 GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
    583                                              const SkMatrix& viewMatrix,
    584                                              const SkRect& oval,
    585                                              const SkStrokeRec& stroke,
    586                                              GrShaderCaps* shaderCaps) {
    587     // we can draw circles
    588     if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
    589         return CreateCircleBatch(color, viewMatrix, oval, stroke);
    590     }
    591 
    592     // if we have shader derivative support, render as device-independent
    593     if (shaderCaps->shaderDerivativeSupport()) {
    594         return CreateDIEllipseBatch(color, viewMatrix, oval, stroke);
    595     }
    596 
    597     // otherwise axis-aligned ellipses only
    598     if (viewMatrix.rectStaysRect()) {
    599         return CreateEllipseBatch(color, viewMatrix, oval, stroke);
    600     }
    601 
    602     return nullptr;
    603 }
    604 
    605 ///////////////////////////////////////////////////////////////////////////////
    606 
    607 class CircleBatch : public GrVertexBatch {
    608 public:
    609     DEFINE_BATCH_CLASS_ID
    610 
    611     struct Geometry {
    612         SkMatrix fViewMatrix;
    613         SkRect fDevBounds;
    614         SkScalar fInnerRadius;
    615         SkScalar fOuterRadius;
    616         GrColor fColor;
    617         bool fStroke;
    618     };
    619 
    620     static GrDrawBatch* Create(const Geometry& geometry) { return new CircleBatch(geometry); }
    621 
    622     const char* name() const override { return "CircleBatch"; }
    623 
    624     SkString dumpInfo() const override {
    625         SkString string;
    626         for (int i = 0; i < fGeoData.count(); ++i) {
    627             string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
    628                            "InnerRad: %.2f, OuterRad: %.2f\n",
    629                            fGeoData[i].fColor,
    630                            fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
    631                            fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
    632                            fGeoData[i].fInnerRadius,
    633                            fGeoData[i].fOuterRadius);
    634         }
    635         string.append(INHERITED::dumpInfo());
    636         return string;
    637     }
    638 
    639     void computePipelineOptimizations(GrInitInvariantOutput* color,
    640                                       GrInitInvariantOutput* coverage,
    641                                       GrBatchToXPOverrides* overrides) const override {
    642         // When this is called on a batch, there is only one geometry bundle
    643         color->setKnownFourComponents(fGeoData[0].fColor);
    644         coverage->setUnknownSingleComponent();
    645     }
    646 
    647 private:
    648     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
    649         // Handle any color overrides
    650         if (!overrides.readsColor()) {
    651             fGeoData[0].fColor = GrColor_ILLEGAL;
    652         }
    653         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
    654 
    655         // setup batch properties
    656         fBatch.fColorIgnored = !overrides.readsColor();
    657         fBatch.fColor = fGeoData[0].fColor;
    658         fBatch.fStroke = fGeoData[0].fStroke;
    659         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
    660         fBatch.fCoverageIgnored = !overrides.readsCoverage();
    661     }
    662 
    663     void onPrepareDraws(Target* target) const override {
    664         SkMatrix invert;
    665         if (!this->viewMatrix().invert(&invert)) {
    666             return;
    667         }
    668 
    669         // Setup geometry processor
    670         SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
    671                                                                       this->stroke(),
    672                                                                       invert,
    673                                                                       this->usesLocalCoords()));
    674 
    675         target->initDraw(gp, this->pipeline());
    676 
    677         int instanceCount = fGeoData.count();
    678         size_t vertexStride = gp->getVertexStride();
    679         SkASSERT(vertexStride == sizeof(CircleVertex));
    680         QuadHelper helper;
    681         CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
    682                                                                           instanceCount));
    683         if (!verts) {
    684             return;
    685         }
    686 
    687         for (int i = 0; i < instanceCount; i++) {
    688             const Geometry& geom = fGeoData[i];
    689 
    690             GrColor color = geom.fColor;
    691             SkScalar innerRadius = geom.fInnerRadius;
    692             SkScalar outerRadius = geom.fOuterRadius;
    693 
    694             const SkRect& bounds = geom.fDevBounds;
    695 
    696             // The inner radius in the vertex data must be specified in normalized space.
    697             innerRadius = innerRadius / outerRadius;
    698             verts[0].fPos = SkPoint::Make(bounds.fLeft,  bounds.fTop);
    699             verts[0].fColor = color;
    700             verts[0].fOffset = SkPoint::Make(-1, -1);
    701             verts[0].fOuterRadius = outerRadius;
    702             verts[0].fInnerRadius = innerRadius;
    703 
    704             verts[1].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
    705             verts[1].fColor = color;
    706             verts[1].fOffset = SkPoint::Make(-1, 1);
    707             verts[1].fOuterRadius = outerRadius;
    708             verts[1].fInnerRadius = innerRadius;
    709 
    710             verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
    711             verts[2].fColor = color;
    712             verts[2].fOffset = SkPoint::Make(1, 1);
    713             verts[2].fOuterRadius = outerRadius;
    714             verts[2].fInnerRadius = innerRadius;
    715 
    716             verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
    717             verts[3].fColor = color;
    718             verts[3].fOffset = SkPoint::Make(1, -1);
    719             verts[3].fOuterRadius = outerRadius;
    720             verts[3].fInnerRadius = innerRadius;
    721 
    722             verts += kVerticesPerQuad;
    723         }
    724         helper.recordDraw(target);
    725     }
    726 
    727     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
    728 
    729     CircleBatch(const Geometry& geometry) : INHERITED(ClassID()) {
    730         fGeoData.push_back(geometry);
    731 
    732         this->setBounds(geometry.fDevBounds);
    733     }
    734 
    735     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
    736         CircleBatch* that = t->cast<CircleBatch>();
    737         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
    738                                     that->bounds(), caps)) {
    739             return false;
    740         }
    741 
    742         if (this->stroke() != that->stroke()) {
    743             return false;
    744         }
    745 
    746         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
    747         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    748             return false;
    749         }
    750 
    751         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
    752         this->joinBounds(that->bounds());
    753         return true;
    754     }
    755 
    756     GrColor color() const { return fBatch.fColor; }
    757     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
    758     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
    759     bool stroke() const { return fBatch.fStroke; }
    760 
    761     struct BatchTracker {
    762         GrColor fColor;
    763         bool fStroke;
    764         bool fUsesLocalCoords;
    765         bool fColorIgnored;
    766         bool fCoverageIgnored;
    767     };
    768 
    769     BatchTracker fBatch;
    770     SkSTArray<1, Geometry, true> fGeoData;
    771 
    772     typedef GrVertexBatch INHERITED;
    773 };
    774 
    775 static GrDrawBatch* create_circle_batch(GrColor color,
    776                                         const SkMatrix& viewMatrix,
    777                                         const SkRect& circle,
    778                                         const SkStrokeRec& stroke) {
    779     SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
    780     viewMatrix.mapPoints(&center, 1);
    781     SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
    782     SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
    783 
    784     SkStrokeRec::Style style = stroke.getStyle();
    785     bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
    786                         SkStrokeRec::kHairline_Style == style;
    787     bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
    788 
    789     SkScalar innerRadius = 0.0f;
    790     SkScalar outerRadius = radius;
    791     SkScalar halfWidth = 0;
    792     if (hasStroke) {
    793         if (SkScalarNearlyZero(strokeWidth)) {
    794             halfWidth = SK_ScalarHalf;
    795         } else {
    796             halfWidth = SkScalarHalf(strokeWidth);
    797         }
    798 
    799         outerRadius += halfWidth;
    800         if (isStrokeOnly) {
    801             innerRadius = radius - halfWidth;
    802         }
    803     }
    804 
    805     // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
    806     // computation because the computed alpha is zero, rather than 50%, at the radius.
    807     // Second, the outer radius is used to compute the verts of the bounding box that is rendered
    808     // and the outset ensures the box will cover all partially covered by the circle.
    809     outerRadius += SK_ScalarHalf;
    810     innerRadius -= SK_ScalarHalf;
    811 
    812     CircleBatch::Geometry geometry;
    813     geometry.fViewMatrix = viewMatrix;
    814     geometry.fColor = color;
    815     geometry.fInnerRadius = innerRadius;
    816     geometry.fOuterRadius = outerRadius;
    817     geometry.fStroke = isStrokeOnly && innerRadius > 0;
    818     geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
    819                                            center.fX + outerRadius, center.fY + outerRadius);
    820 
    821     return CircleBatch::Create(geometry);
    822 }
    823 
    824 GrDrawBatch* GrOvalRenderer::CreateCircleBatch(GrColor color,
    825                                                const SkMatrix& viewMatrix,
    826                                                const SkRect& circle,
    827                                                const SkStrokeRec& stroke) {
    828     return create_circle_batch(color, viewMatrix, circle, stroke);
    829 }
    830 
    831 ///////////////////////////////////////////////////////////////////////////////
    832 
    833 class EllipseBatch : public GrVertexBatch {
    834 public:
    835     DEFINE_BATCH_CLASS_ID
    836 
    837     struct Geometry {
    838         SkMatrix fViewMatrix;
    839         SkRect fDevBounds;
    840         SkScalar fXRadius;
    841         SkScalar fYRadius;
    842         SkScalar fInnerXRadius;
    843         SkScalar fInnerYRadius;
    844         GrColor fColor;
    845         bool fStroke;
    846     };
    847 
    848     static GrDrawBatch* Create(const Geometry& geometry) { return new EllipseBatch(geometry); }
    849 
    850     const char* name() const override { return "EllipseBatch"; }
    851 
    852     void computePipelineOptimizations(GrInitInvariantOutput* color,
    853                                       GrInitInvariantOutput* coverage,
    854                                       GrBatchToXPOverrides* overrides) const override {
    855         // When this is called on a batch, there is only one geometry bundle
    856         color->setKnownFourComponents(fGeoData[0].fColor);
    857         coverage->setUnknownSingleComponent();
    858     }
    859 
    860 private:
    861     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
    862         // Handle any color overrides
    863         if (!overrides.readsCoverage()) {
    864             fGeoData[0].fColor = GrColor_ILLEGAL;
    865         }
    866         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
    867 
    868         // setup batch properties
    869         fBatch.fColorIgnored = !overrides.readsColor();
    870         fBatch.fColor = fGeoData[0].fColor;
    871         fBatch.fStroke = fGeoData[0].fStroke;
    872         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
    873         fBatch.fCoverageIgnored = !overrides.readsCoverage();
    874     }
    875 
    876     void onPrepareDraws(Target* target) const override {
    877         SkMatrix invert;
    878         if (!this->viewMatrix().invert(&invert)) {
    879             return;
    880         }
    881 
    882         // Setup geometry processor
    883         SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
    884                                                                        this->stroke(),
    885                                                                        invert,
    886                                                                        this->usesLocalCoords()));
    887 
    888         target->initDraw(gp, this->pipeline());
    889 
    890         int instanceCount = fGeoData.count();
    891         QuadHelper helper;
    892         size_t vertexStride = gp->getVertexStride();
    893         SkASSERT(vertexStride == sizeof(EllipseVertex));
    894         EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
    895             helper.init(target, vertexStride, instanceCount));
    896         if (!verts) {
    897             return;
    898         }
    899 
    900         for (int i = 0; i < instanceCount; i++) {
    901             const Geometry& geom = fGeoData[i];
    902 
    903             GrColor color = geom.fColor;
    904             SkScalar xRadius = geom.fXRadius;
    905             SkScalar yRadius = geom.fYRadius;
    906 
    907             // Compute the reciprocals of the radii here to save time in the shader
    908             SkScalar xRadRecip = SkScalarInvert(xRadius);
    909             SkScalar yRadRecip = SkScalarInvert(yRadius);
    910             SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
    911             SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
    912 
    913             const SkRect& bounds = geom.fDevBounds;
    914 
    915             // The inner radius in the vertex data must be specified in normalized space.
    916             verts[0].fPos = SkPoint::Make(bounds.fLeft,  bounds.fTop);
    917             verts[0].fColor = color;
    918             verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
    919             verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    920             verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    921 
    922             verts[1].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
    923             verts[1].fColor = color;
    924             verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
    925             verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    926             verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    927 
    928             verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
    929             verts[2].fColor = color;
    930             verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
    931             verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    932             verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    933 
    934             verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
    935             verts[3].fColor = color;
    936             verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
    937             verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    938             verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    939 
    940             verts += kVerticesPerQuad;
    941         }
    942         helper.recordDraw(target);
    943     }
    944 
    945     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
    946 
    947     EllipseBatch(const Geometry& geometry) : INHERITED(ClassID()) {
    948         fGeoData.push_back(geometry);
    949 
    950         this->setBounds(geometry.fDevBounds);
    951     }
    952 
    953     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
    954         EllipseBatch* that = t->cast<EllipseBatch>();
    955 
    956         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
    957                                     that->bounds(), caps)) {
    958             return false;
    959         }
    960 
    961         if (this->stroke() != that->stroke()) {
    962             return false;
    963         }
    964 
    965         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
    966         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    967             return false;
    968         }
    969 
    970         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
    971         this->joinBounds(that->bounds());
    972         return true;
    973     }
    974 
    975     GrColor color() const { return fBatch.fColor; }
    976     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
    977     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
    978     bool stroke() const { return fBatch.fStroke; }
    979 
    980     struct BatchTracker {
    981         GrColor fColor;
    982         bool fStroke;
    983         bool fUsesLocalCoords;
    984         bool fColorIgnored;
    985         bool fCoverageIgnored;
    986     };
    987 
    988     BatchTracker fBatch;
    989     SkSTArray<1, Geometry, true> fGeoData;
    990 
    991     typedef GrVertexBatch INHERITED;
    992 };
    993 
    994 static GrDrawBatch* create_ellipse_batch(GrColor color,
    995                                          const SkMatrix& viewMatrix,
    996                                          const SkRect& ellipse,
    997                                          const SkStrokeRec& stroke) {
    998     SkASSERT(viewMatrix.rectStaysRect());
    999 
   1000     // do any matrix crunching before we reset the draw state for device coords
   1001     SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
   1002     viewMatrix.mapPoints(&center, 1);
   1003     SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
   1004     SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
   1005     SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
   1006                                    viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
   1007     SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
   1008                                    viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
   1009 
   1010     // do (potentially) anisotropic mapping of stroke
   1011     SkVector scaledStroke;
   1012     SkScalar strokeWidth = stroke.getWidth();
   1013     scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
   1014                                                viewMatrix[SkMatrix::kMSkewY]));
   1015     scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
   1016                                                viewMatrix[SkMatrix::kMScaleY]));
   1017 
   1018     SkStrokeRec::Style style = stroke.getStyle();
   1019     bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
   1020                         SkStrokeRec::kHairline_Style == style;
   1021     bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
   1022 
   1023     SkScalar innerXRadius = 0;
   1024     SkScalar innerYRadius = 0;
   1025     if (hasStroke) {
   1026         if (SkScalarNearlyZero(scaledStroke.length())) {
   1027             scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
   1028         } else {
   1029             scaledStroke.scale(SK_ScalarHalf);
   1030         }
   1031 
   1032         // we only handle thick strokes for near-circular ellipses
   1033         if (scaledStroke.length() > SK_ScalarHalf &&
   1034             (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
   1035             return nullptr;
   1036         }
   1037 
   1038         // we don't handle it if curvature of the stroke is less than curvature of the ellipse
   1039         if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
   1040             scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
   1041             return nullptr;
   1042         }
   1043 
   1044         // this is legit only if scale & translation (which should be the case at the moment)
   1045         if (isStrokeOnly) {
   1046             innerXRadius = xRadius - scaledStroke.fX;
   1047             innerYRadius = yRadius - scaledStroke.fY;
   1048         }
   1049 
   1050         xRadius += scaledStroke.fX;
   1051         yRadius += scaledStroke.fY;
   1052     }
   1053 
   1054     // We've extended the outer x radius out half a pixel to antialias.
   1055     // This will also expand the rect so all the pixels will be captured.
   1056     // TODO: Consider if we should use sqrt(2)/2 instead
   1057     xRadius += SK_ScalarHalf;
   1058     yRadius += SK_ScalarHalf;
   1059 
   1060     EllipseBatch::Geometry geometry;
   1061     geometry.fViewMatrix = viewMatrix;
   1062     geometry.fColor = color;
   1063     geometry.fXRadius = xRadius;
   1064     geometry.fYRadius = yRadius;
   1065     geometry.fInnerXRadius = innerXRadius;
   1066     geometry.fInnerYRadius = innerYRadius;
   1067     geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
   1068     geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
   1069                                            center.fX + xRadius, center.fY + yRadius);
   1070 
   1071     return EllipseBatch::Create(geometry);
   1072 }
   1073 
   1074 GrDrawBatch* GrOvalRenderer::CreateEllipseBatch(GrColor color,
   1075                                                 const SkMatrix& viewMatrix,
   1076                                                 const SkRect& ellipse,
   1077                                                 const SkStrokeRec& stroke) {
   1078     return create_ellipse_batch(color, viewMatrix, ellipse, stroke);
   1079 }
   1080 
   1081 /////////////////////////////////////////////////////////////////////////////////////////////////
   1082 
   1083 class DIEllipseBatch : public GrVertexBatch {
   1084 public:
   1085     DEFINE_BATCH_CLASS_ID
   1086 
   1087     struct Geometry {
   1088         SkMatrix fViewMatrix;
   1089         SkRect fBounds;
   1090         SkScalar fXRadius;
   1091         SkScalar fYRadius;
   1092         SkScalar fInnerXRadius;
   1093         SkScalar fInnerYRadius;
   1094         SkScalar fGeoDx;
   1095         SkScalar fGeoDy;
   1096         GrColor fColor;
   1097         DIEllipseEdgeEffect::Mode fMode;
   1098     };
   1099 
   1100     static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
   1101         return new DIEllipseBatch(geometry, bounds);
   1102     }
   1103 
   1104     const char* name() const override { return "DIEllipseBatch"; }
   1105 
   1106     void computePipelineOptimizations(GrInitInvariantOutput* color,
   1107                                       GrInitInvariantOutput* coverage,
   1108                                       GrBatchToXPOverrides* overrides) const override {
   1109         // When this is called on a batch, there is only one geometry bundle
   1110         color->setKnownFourComponents(fGeoData[0].fColor);
   1111         coverage->setUnknownSingleComponent();
   1112     }
   1113 
   1114 private:
   1115 
   1116     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
   1117         // Handle any color overrides
   1118         if (!overrides.readsColor()) {
   1119             fGeoData[0].fColor = GrColor_ILLEGAL;
   1120         }
   1121         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
   1122 
   1123         // setup batch properties
   1124         fBatch.fColorIgnored = !overrides.readsColor();
   1125         fBatch.fColor = fGeoData[0].fColor;
   1126         fBatch.fMode = fGeoData[0].fMode;
   1127         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
   1128         fBatch.fCoverageIgnored = !overrides.readsCoverage();
   1129     }
   1130 
   1131     void onPrepareDraws(Target* target) const override {
   1132         // Setup geometry processor
   1133         SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
   1134                                                                          this->viewMatrix(),
   1135                                                                          this->mode(),
   1136                                                                          this->usesLocalCoords()));
   1137 
   1138         target->initDraw(gp, this->pipeline());
   1139 
   1140         int instanceCount = fGeoData.count();
   1141         size_t vertexStride = gp->getVertexStride();
   1142         SkASSERT(vertexStride == sizeof(DIEllipseVertex));
   1143         QuadHelper helper;
   1144         DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
   1145             helper.init(target, vertexStride, instanceCount));
   1146         if (!verts) {
   1147             return;
   1148         }
   1149 
   1150         for (int i = 0; i < instanceCount; i++) {
   1151             const Geometry& geom = fGeoData[i];
   1152 
   1153             GrColor color = geom.fColor;
   1154             SkScalar xRadius = geom.fXRadius;
   1155             SkScalar yRadius = geom.fYRadius;
   1156 
   1157             const SkRect& bounds = geom.fBounds;
   1158 
   1159             // This adjusts the "radius" to include the half-pixel border
   1160             SkScalar offsetDx = geom.fGeoDx / xRadius;
   1161             SkScalar offsetDy = geom.fGeoDy / yRadius;
   1162 
   1163             SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
   1164             SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
   1165 
   1166             verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
   1167             verts[0].fColor = color;
   1168             verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
   1169             verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
   1170 
   1171             verts[1].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
   1172             verts[1].fColor = color;
   1173             verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
   1174             verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
   1175 
   1176             verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
   1177             verts[2].fColor = color;
   1178             verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
   1179             verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
   1180 
   1181             verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
   1182             verts[3].fColor = color;
   1183             verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
   1184             verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
   1185 
   1186             verts += kVerticesPerQuad;
   1187         }
   1188         helper.recordDraw(target);
   1189     }
   1190 
   1191     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
   1192 
   1193     DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(ClassID()) {
   1194         fGeoData.push_back(geometry);
   1195 
   1196         this->setBounds(bounds);
   1197     }
   1198 
   1199     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
   1200         DIEllipseBatch* that = t->cast<DIEllipseBatch>();
   1201         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
   1202                                     that->bounds(), caps)) {
   1203             return false;
   1204         }
   1205 
   1206         if (this->mode() != that->mode()) {
   1207             return false;
   1208         }
   1209 
   1210         // TODO rewrite to allow positioning on CPU
   1211         if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
   1212             return false;
   1213         }
   1214 
   1215         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
   1216         this->joinBounds(that->bounds());
   1217         return true;
   1218     }
   1219 
   1220     GrColor color() const { return fBatch.fColor; }
   1221     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
   1222     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
   1223     DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
   1224 
   1225     struct BatchTracker {
   1226         GrColor fColor;
   1227         DIEllipseEdgeEffect::Mode fMode;
   1228         bool fUsesLocalCoords;
   1229         bool fColorIgnored;
   1230         bool fCoverageIgnored;
   1231     };
   1232 
   1233     BatchTracker fBatch;
   1234     SkSTArray<1, Geometry, true> fGeoData;
   1235 
   1236     typedef GrVertexBatch INHERITED;
   1237 };
   1238 
   1239 static GrDrawBatch* create_diellipse_batch(GrColor color,
   1240                                            const SkMatrix& viewMatrix,
   1241                                            const SkRect& ellipse,
   1242                                            const SkStrokeRec& stroke) {
   1243     SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
   1244     SkScalar xRadius = SkScalarHalf(ellipse.width());
   1245     SkScalar yRadius = SkScalarHalf(ellipse.height());
   1246 
   1247     SkStrokeRec::Style style = stroke.getStyle();
   1248     DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
   1249                                     DIEllipseEdgeEffect::kStroke :
   1250                                     (SkStrokeRec::kHairline_Style == style) ?
   1251                                     DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
   1252 
   1253     SkScalar innerXRadius = 0;
   1254     SkScalar innerYRadius = 0;
   1255     if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
   1256         SkScalar strokeWidth = stroke.getWidth();
   1257 
   1258         if (SkScalarNearlyZero(strokeWidth)) {
   1259             strokeWidth = SK_ScalarHalf;
   1260         } else {
   1261             strokeWidth *= SK_ScalarHalf;
   1262         }
   1263 
   1264         // we only handle thick strokes for near-circular ellipses
   1265         if (strokeWidth > SK_ScalarHalf &&
   1266             (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
   1267             return nullptr;
   1268         }
   1269 
   1270         // we don't handle it if curvature of the stroke is less than curvature of the ellipse
   1271         if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
   1272             strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
   1273             return nullptr;
   1274         }
   1275 
   1276         // set inner radius (if needed)
   1277         if (SkStrokeRec::kStroke_Style == style) {
   1278             innerXRadius = xRadius - strokeWidth;
   1279             innerYRadius = yRadius - strokeWidth;
   1280         }
   1281 
   1282         xRadius += strokeWidth;
   1283         yRadius += strokeWidth;
   1284     }
   1285     if (DIEllipseEdgeEffect::kStroke == mode) {
   1286         mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
   1287                                                         DIEllipseEdgeEffect::kFill;
   1288     }
   1289 
   1290     // This expands the outer rect so that after CTM we end up with a half-pixel border
   1291     SkScalar a = viewMatrix[SkMatrix::kMScaleX];
   1292     SkScalar b = viewMatrix[SkMatrix::kMSkewX];
   1293     SkScalar c = viewMatrix[SkMatrix::kMSkewY];
   1294     SkScalar d = viewMatrix[SkMatrix::kMScaleY];
   1295     SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
   1296     SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
   1297 
   1298     DIEllipseBatch::Geometry geometry;
   1299     geometry.fViewMatrix = viewMatrix;
   1300     geometry.fColor = color;
   1301     geometry.fXRadius = xRadius;
   1302     geometry.fYRadius = yRadius;
   1303     geometry.fInnerXRadius = innerXRadius;
   1304     geometry.fInnerYRadius = innerYRadius;
   1305     geometry.fGeoDx = geoDx;
   1306     geometry.fGeoDy = geoDy;
   1307     geometry.fMode = mode;
   1308     geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
   1309                                         center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
   1310 
   1311     SkRect devBounds = geometry.fBounds;
   1312     viewMatrix.mapRect(&devBounds);
   1313     return DIEllipseBatch::Create(geometry, devBounds);
   1314 }
   1315 
   1316 GrDrawBatch* GrOvalRenderer::CreateDIEllipseBatch(GrColor color,
   1317                                                   const SkMatrix& viewMatrix,
   1318                                                   const SkRect& ellipse,
   1319                                                   const SkStrokeRec& stroke) {
   1320     return create_diellipse_batch(color, viewMatrix, ellipse, stroke);
   1321 }
   1322 
   1323 ///////////////////////////////////////////////////////////////////////////////
   1324 
   1325 static const uint16_t gRRectIndices[] = {
   1326     // corners
   1327     0, 1, 5, 0, 5, 4,
   1328     2, 3, 7, 2, 7, 6,
   1329     8, 9, 13, 8, 13, 12,
   1330     10, 11, 15, 10, 15, 14,
   1331 
   1332     // edges
   1333     1, 2, 6, 1, 6, 5,
   1334     4, 5, 9, 4, 9, 8,
   1335     6, 7, 11, 6, 11, 10,
   1336     9, 10, 14, 9, 14, 13,
   1337 
   1338     // center
   1339     // we place this at the end so that we can ignore these indices when rendering stroke-only
   1340     5, 6, 10, 5, 10, 9
   1341 };
   1342 
   1343 static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
   1344 static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
   1345 static const int kVertsPerRRect = 16;
   1346 static const int kNumRRectsInIndexBuffer = 256;
   1347 
   1348 GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
   1349 GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
   1350 static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
   1351                                                    GrResourceProvider* resourceProvider) {
   1352     GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
   1353     GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
   1354     if (strokeOnly) {
   1355         return resourceProvider->findOrCreateInstancedIndexBuffer(
   1356             gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
   1357             gStrokeRRectOnlyIndexBufferKey);
   1358     } else {
   1359         return resourceProvider->findOrCreateInstancedIndexBuffer(
   1360             gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
   1361             gRRectOnlyIndexBufferKey);
   1362 
   1363     }
   1364 }
   1365 
   1366 ///////////////////////////////////////////////////////////////////////////////////////////////////
   1367 
   1368 class RRectCircleRendererBatch : public GrVertexBatch {
   1369 public:
   1370     DEFINE_BATCH_CLASS_ID
   1371 
   1372     struct Geometry {
   1373         SkMatrix fViewMatrix;
   1374         SkRect fDevBounds;
   1375         SkScalar fInnerRadius;
   1376         SkScalar fOuterRadius;
   1377         GrColor fColor;
   1378         bool fStroke;
   1379     };
   1380 
   1381     static GrDrawBatch* Create(const Geometry& geometry) {
   1382         return new RRectCircleRendererBatch(geometry);
   1383     }
   1384 
   1385     const char* name() const override { return "RRectCircleBatch"; }
   1386 
   1387     void computePipelineOptimizations(GrInitInvariantOutput* color,
   1388                                       GrInitInvariantOutput* coverage,
   1389                                       GrBatchToXPOverrides* overrides) const override {
   1390         // When this is called on a batch, there is only one geometry bundle
   1391         color->setKnownFourComponents(fGeoData[0].fColor);
   1392         coverage->setUnknownSingleComponent();
   1393     }
   1394 
   1395 private:
   1396     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
   1397         // Handle any color overrides
   1398         if (!overrides.readsColor()) {
   1399             fGeoData[0].fColor = GrColor_ILLEGAL;
   1400         }
   1401         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
   1402 
   1403         // setup batch properties
   1404         fBatch.fColorIgnored = !overrides.readsColor();
   1405         fBatch.fColor = fGeoData[0].fColor;
   1406         fBatch.fStroke = fGeoData[0].fStroke;
   1407         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
   1408         fBatch.fCoverageIgnored = !overrides.readsCoverage();
   1409     }
   1410 
   1411     void onPrepareDraws(Target* target) const override {
   1412         // reset to device coordinates
   1413         SkMatrix invert;
   1414         if (!this->viewMatrix().invert(&invert)) {
   1415             SkDebugf("Failed to invert\n");
   1416             return;
   1417         }
   1418 
   1419         // Setup geometry processor
   1420         SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
   1421                                                                       this->stroke(),
   1422                                                                       invert,
   1423                                                                       this->usesLocalCoords()));
   1424 
   1425         target->initDraw(gp, this->pipeline());
   1426 
   1427         int instanceCount = fGeoData.count();
   1428         size_t vertexStride = gp->getVertexStride();
   1429         SkASSERT(vertexStride == sizeof(CircleVertex));
   1430 
   1431         // drop out the middle quad if we're stroked
   1432         int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
   1433         SkAutoTUnref<const GrIndexBuffer> indexBuffer(
   1434             ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
   1435 
   1436         InstancedHelper helper;
   1437         CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
   1438             kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
   1439             indicesPerInstance, instanceCount));
   1440         if (!verts || !indexBuffer) {
   1441             SkDebugf("Could not allocate vertices\n");
   1442             return;
   1443         }
   1444 
   1445         for (int i = 0; i < instanceCount; i++) {
   1446             const Geometry& args = fGeoData[i];
   1447 
   1448             GrColor color = args.fColor;
   1449             SkScalar outerRadius = args.fOuterRadius;
   1450 
   1451             const SkRect& bounds = args.fDevBounds;
   1452 
   1453             SkScalar yCoords[4] = {
   1454                 bounds.fTop,
   1455                 bounds.fTop + outerRadius,
   1456                 bounds.fBottom - outerRadius,
   1457                 bounds.fBottom
   1458             };
   1459 
   1460             SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
   1461             // The inner radius in the vertex data must be specified in normalized space.
   1462             SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
   1463             for (int i = 0; i < 4; ++i) {
   1464                 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
   1465                 verts->fColor = color;
   1466                 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
   1467                 verts->fOuterRadius = outerRadius;
   1468                 verts->fInnerRadius = innerRadius;
   1469                 verts++;
   1470 
   1471                 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
   1472                 verts->fColor = color;
   1473                 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
   1474                 verts->fOuterRadius = outerRadius;
   1475                 verts->fInnerRadius = innerRadius;
   1476                 verts++;
   1477 
   1478                 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
   1479                 verts->fColor = color;
   1480                 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
   1481                 verts->fOuterRadius = outerRadius;
   1482                 verts->fInnerRadius = innerRadius;
   1483                 verts++;
   1484 
   1485                 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
   1486                 verts->fColor = color;
   1487                 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
   1488                 verts->fOuterRadius = outerRadius;
   1489                 verts->fInnerRadius = innerRadius;
   1490                 verts++;
   1491             }
   1492         }
   1493 
   1494         helper.recordDraw(target);
   1495     }
   1496 
   1497     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
   1498 
   1499     RRectCircleRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
   1500         fGeoData.push_back(geometry);
   1501 
   1502         this->setBounds(geometry.fDevBounds);
   1503     }
   1504 
   1505     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
   1506         RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
   1507         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
   1508                                     that->bounds(), caps)) {
   1509             return false;
   1510         }
   1511 
   1512         if (this->stroke() != that->stroke()) {
   1513             return false;
   1514         }
   1515 
   1516         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
   1517         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
   1518             return false;
   1519         }
   1520 
   1521         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
   1522         this->joinBounds(that->bounds());
   1523         return true;
   1524     }
   1525 
   1526     GrColor color() const { return fBatch.fColor; }
   1527     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
   1528     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
   1529     bool stroke() const { return fBatch.fStroke; }
   1530 
   1531     struct BatchTracker {
   1532         GrColor fColor;
   1533         bool fStroke;
   1534         bool fUsesLocalCoords;
   1535         bool fColorIgnored;
   1536         bool fCoverageIgnored;
   1537     };
   1538 
   1539     BatchTracker fBatch;
   1540     SkSTArray<1, Geometry, true> fGeoData;
   1541 
   1542     typedef GrVertexBatch INHERITED;
   1543 };
   1544 
   1545 class RRectEllipseRendererBatch : public GrVertexBatch {
   1546 public:
   1547     DEFINE_BATCH_CLASS_ID
   1548 
   1549     struct Geometry {
   1550         SkMatrix fViewMatrix;
   1551         SkRect fDevBounds;
   1552         SkScalar fXRadius;
   1553         SkScalar fYRadius;
   1554         SkScalar fInnerXRadius;
   1555         SkScalar fInnerYRadius;
   1556         GrColor fColor;
   1557         bool fStroke;
   1558     };
   1559 
   1560     static GrDrawBatch* Create(const Geometry& geometry) {
   1561         return new RRectEllipseRendererBatch(geometry);
   1562     }
   1563 
   1564     const char* name() const override { return "RRectEllipseRendererBatch"; }
   1565 
   1566     void computePipelineOptimizations(GrInitInvariantOutput* color,
   1567                                       GrInitInvariantOutput* coverage,
   1568                                       GrBatchToXPOverrides* overrides) const override {
   1569         // When this is called on a batch, there is only one geometry bundle
   1570         color->setKnownFourComponents(fGeoData[0].fColor);
   1571         coverage->setUnknownSingleComponent();
   1572     }
   1573 
   1574 private:
   1575     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
   1576         // Handle any color overrides
   1577         if (!overrides.readsColor()) {
   1578             fGeoData[0].fColor = GrColor_ILLEGAL;
   1579         }
   1580         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
   1581 
   1582         // setup batch properties
   1583         fBatch.fColorIgnored = !overrides.readsColor();
   1584         fBatch.fColor = fGeoData[0].fColor;
   1585         fBatch.fStroke = fGeoData[0].fStroke;
   1586         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
   1587         fBatch.fCoverageIgnored = !overrides.readsCoverage();
   1588     }
   1589 
   1590     void onPrepareDraws(Target* target) const override {
   1591         // reset to device coordinates
   1592         SkMatrix invert;
   1593         if (!this->viewMatrix().invert(&invert)) {
   1594             SkDebugf("Failed to invert\n");
   1595             return;
   1596         }
   1597 
   1598         // Setup geometry processor
   1599         SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
   1600                                                                        this->stroke(),
   1601                                                                        invert,
   1602                                                                        this->usesLocalCoords()));
   1603 
   1604         target->initDraw(gp, this->pipeline());
   1605 
   1606         int instanceCount = fGeoData.count();
   1607         size_t vertexStride = gp->getVertexStride();
   1608         SkASSERT(vertexStride == sizeof(EllipseVertex));
   1609 
   1610         // drop out the middle quad if we're stroked
   1611         int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
   1612         SkAutoTUnref<const GrIndexBuffer> indexBuffer(
   1613             ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
   1614 
   1615         InstancedHelper helper;
   1616         EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
   1617             helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
   1618             kVertsPerRRect, indicesPerInstance, instanceCount));
   1619         if (!verts || !indexBuffer) {
   1620             SkDebugf("Could not allocate vertices\n");
   1621             return;
   1622         }
   1623 
   1624         for (int i = 0; i < instanceCount; i++) {
   1625             const Geometry& args = fGeoData[i];
   1626 
   1627             GrColor color = args.fColor;
   1628 
   1629             // Compute the reciprocals of the radii here to save time in the shader
   1630             SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
   1631             SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
   1632             SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
   1633             SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
   1634 
   1635             // Extend the radii out half a pixel to antialias.
   1636             SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
   1637             SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
   1638 
   1639             const SkRect& bounds = args.fDevBounds;
   1640 
   1641             SkScalar yCoords[4] = {
   1642                 bounds.fTop,
   1643                 bounds.fTop + yOuterRadius,
   1644                 bounds.fBottom - yOuterRadius,
   1645                 bounds.fBottom
   1646             };
   1647             SkScalar yOuterOffsets[4] = {
   1648                 yOuterRadius,
   1649                 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
   1650                 SK_ScalarNearlyZero,
   1651                 yOuterRadius
   1652             };
   1653 
   1654             for (int i = 0; i < 4; ++i) {
   1655                 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
   1656                 verts->fColor = color;
   1657                 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
   1658                 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
   1659                 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
   1660                 verts++;
   1661 
   1662                 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
   1663                 verts->fColor = color;
   1664                 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
   1665                 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
   1666                 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
   1667                 verts++;
   1668 
   1669                 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
   1670                 verts->fColor = color;
   1671                 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
   1672                 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
   1673                 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
   1674                 verts++;
   1675 
   1676                 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
   1677                 verts->fColor = color;
   1678                 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
   1679                 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
   1680                 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
   1681                 verts++;
   1682             }
   1683         }
   1684         helper.recordDraw(target);
   1685     }
   1686 
   1687     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
   1688 
   1689     RRectEllipseRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
   1690         fGeoData.push_back(geometry);
   1691 
   1692         this->setBounds(geometry.fDevBounds);
   1693     }
   1694 
   1695     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
   1696         RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
   1697 
   1698         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
   1699                                     that->bounds(), caps)) {
   1700             return false;
   1701         }
   1702 
   1703         if (this->stroke() != that->stroke()) {
   1704             return false;
   1705         }
   1706 
   1707         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
   1708         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
   1709             return false;
   1710         }
   1711 
   1712         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
   1713         this->joinBounds(that->bounds());
   1714         return true;
   1715     }
   1716 
   1717     GrColor color() const { return fBatch.fColor; }
   1718     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
   1719     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
   1720     bool stroke() const { return fBatch.fStroke; }
   1721 
   1722     struct BatchTracker {
   1723         GrColor fColor;
   1724         bool fStroke;
   1725         bool fUsesLocalCoords;
   1726         bool fColorIgnored;
   1727         bool fCoverageIgnored;
   1728     };
   1729 
   1730     BatchTracker fBatch;
   1731     SkSTArray<1, Geometry, true> fGeoData;
   1732 
   1733     typedef GrVertexBatch INHERITED;
   1734 };
   1735 
   1736 static GrDrawBatch* create_rrect_batch(GrColor color,
   1737                                        const SkMatrix& viewMatrix,
   1738                                        const SkRRect& rrect,
   1739                                        const SkStrokeRec& stroke) {
   1740     SkASSERT(viewMatrix.rectStaysRect());
   1741     SkASSERT(rrect.isSimple());
   1742     SkASSERT(!rrect.isOval());
   1743 
   1744     // RRect batchs only handle simple, but not too simple, rrects
   1745     // do any matrix crunching before we reset the draw state for device coords
   1746     const SkRect& rrectBounds = rrect.getBounds();
   1747     SkRect bounds;
   1748     viewMatrix.mapRect(&bounds, rrectBounds);
   1749 
   1750     SkVector radii = rrect.getSimpleRadii();
   1751     SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
   1752                                    viewMatrix[SkMatrix::kMSkewY]*radii.fY);
   1753     SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
   1754                                    viewMatrix[SkMatrix::kMScaleY]*radii.fY);
   1755 
   1756     SkStrokeRec::Style style = stroke.getStyle();
   1757 
   1758     // do (potentially) anisotropic mapping of stroke
   1759     SkVector scaledStroke;
   1760     SkScalar strokeWidth = stroke.getWidth();
   1761 
   1762     bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
   1763                         SkStrokeRec::kHairline_Style == style;
   1764     bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
   1765 
   1766     if (hasStroke) {
   1767         if (SkStrokeRec::kHairline_Style == style) {
   1768             scaledStroke.set(1, 1);
   1769         } else {
   1770             scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
   1771                                                        viewMatrix[SkMatrix::kMSkewY]));
   1772             scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
   1773                                                        viewMatrix[SkMatrix::kMScaleY]));
   1774         }
   1775 
   1776         // if half of strokewidth is greater than radius, we don't handle that right now
   1777         if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
   1778             return nullptr;
   1779         }
   1780     }
   1781 
   1782     // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
   1783     // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
   1784     // patch will have fractional coverage. This only matters when the interior is actually filled.
   1785     // We could consider falling back to rect rendering here, since a tiny radius is
   1786     // indistinguishable from a square corner.
   1787     if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
   1788         return nullptr;
   1789     }
   1790 
   1791     // if the corners are circles, use the circle renderer
   1792     if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
   1793         SkScalar innerRadius = 0.0f;
   1794         SkScalar outerRadius = xRadius;
   1795         SkScalar halfWidth = 0;
   1796         if (hasStroke) {
   1797             if (SkScalarNearlyZero(scaledStroke.fX)) {
   1798                 halfWidth = SK_ScalarHalf;
   1799             } else {
   1800                 halfWidth = SkScalarHalf(scaledStroke.fX);
   1801             }
   1802 
   1803             if (isStrokeOnly) {
   1804                 innerRadius = xRadius - halfWidth;
   1805             }
   1806             outerRadius += halfWidth;
   1807             bounds.outset(halfWidth, halfWidth);
   1808         }
   1809 
   1810         isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
   1811 
   1812         // The radii are outset for two reasons. First, it allows the shader to simply perform
   1813         // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
   1814         // Second, the outer radius is used to compute the verts of the bounding box that is
   1815         // rendered and the outset ensures the box will cover all partially covered by the rrect
   1816         // corners.
   1817         outerRadius += SK_ScalarHalf;
   1818         innerRadius -= SK_ScalarHalf;
   1819 
   1820         // Expand the rect so all the pixels will be captured.
   1821         bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
   1822 
   1823         RRectCircleRendererBatch::Geometry geometry;
   1824         geometry.fViewMatrix = viewMatrix;
   1825         geometry.fColor = color;
   1826         geometry.fInnerRadius = innerRadius;
   1827         geometry.fOuterRadius = outerRadius;
   1828         geometry.fStroke = isStrokeOnly;
   1829         geometry.fDevBounds = bounds;
   1830 
   1831         return RRectCircleRendererBatch::Create(geometry);
   1832     // otherwise we use the ellipse renderer
   1833     } else {
   1834         SkScalar innerXRadius = 0.0f;
   1835         SkScalar innerYRadius = 0.0f;
   1836         if (hasStroke) {
   1837             if (SkScalarNearlyZero(scaledStroke.length())) {
   1838                 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
   1839             } else {
   1840                 scaledStroke.scale(SK_ScalarHalf);
   1841             }
   1842 
   1843             // we only handle thick strokes for near-circular ellipses
   1844             if (scaledStroke.length() > SK_ScalarHalf &&
   1845                 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
   1846                 return nullptr;
   1847             }
   1848 
   1849             // we don't handle it if curvature of the stroke is less than curvature of the ellipse
   1850             if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
   1851                 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
   1852                 return nullptr;
   1853             }
   1854 
   1855             // this is legit only if scale & translation (which should be the case at the moment)
   1856             if (isStrokeOnly) {
   1857                 innerXRadius = xRadius - scaledStroke.fX;
   1858                 innerYRadius = yRadius - scaledStroke.fY;
   1859             }
   1860 
   1861             xRadius += scaledStroke.fX;
   1862             yRadius += scaledStroke.fY;
   1863             bounds.outset(scaledStroke.fX, scaledStroke.fY);
   1864         }
   1865 
   1866         isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
   1867 
   1868         // Expand the rect so all the pixels will be captured.
   1869         bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
   1870 
   1871         RRectEllipseRendererBatch::Geometry geometry;
   1872         geometry.fViewMatrix = viewMatrix;
   1873         geometry.fColor = color;
   1874         geometry.fXRadius = xRadius;
   1875         geometry.fYRadius = yRadius;
   1876         geometry.fInnerXRadius = innerXRadius;
   1877         geometry.fInnerYRadius = innerYRadius;
   1878         geometry.fStroke = isStrokeOnly;
   1879         geometry.fDevBounds = bounds;
   1880 
   1881         return RRectEllipseRendererBatch::Create(geometry);
   1882     }
   1883 }
   1884 
   1885 GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
   1886                                               const SkMatrix& viewMatrix,
   1887                                               const SkRRect& rrect,
   1888                                               const SkStrokeRec& stroke,
   1889                                               GrShaderCaps* shaderCaps) {
   1890     if (rrect.isOval()) {
   1891         return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
   1892     }
   1893 
   1894     if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
   1895         return nullptr;
   1896     }
   1897 
   1898     return create_rrect_batch(color, viewMatrix, rrect, stroke);
   1899 }
   1900 
   1901 ///////////////////////////////////////////////////////////////////////////////////////////////////
   1902 
   1903 #ifdef GR_TEST_UTILS
   1904 
   1905 DRAW_BATCH_TEST_DEFINE(CircleBatch) {
   1906     SkMatrix viewMatrix = GrTest::TestMatrix(random);
   1907     GrColor color = GrRandomColor(random);
   1908     SkRect circle = GrTest::TestSquare(random);
   1909     return create_circle_batch(color, viewMatrix, circle, GrTest::TestStrokeRec(random));
   1910 }
   1911 
   1912 DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
   1913     SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
   1914     GrColor color = GrRandomColor(random);
   1915     SkRect ellipse = GrTest::TestSquare(random);
   1916     return create_ellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
   1917 }
   1918 
   1919 DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
   1920     SkMatrix viewMatrix = GrTest::TestMatrix(random);
   1921     GrColor color = GrRandomColor(random);
   1922     SkRect ellipse = GrTest::TestSquare(random);
   1923     return create_diellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
   1924 }
   1925 
   1926 DRAW_BATCH_TEST_DEFINE(RRectBatch) {
   1927     SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
   1928     GrColor color = GrRandomColor(random);
   1929     const SkRRect& rrect = GrTest::TestRRectSimple(random);
   1930     return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
   1931 }
   1932 
   1933 #endif
   1934