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