| Index: src/gpu/GrAARectRenderer.cpp
 | 
| diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
 | 
| index 46196e2e4ef542d8e3d8a70d8e7fcc377b59b1f7..ead4ea16a37aac51d45ca9e729dca52335f56774 100644
 | 
| --- a/src/gpu/GrAARectRenderer.cpp
 | 
| +++ b/src/gpu/GrAARectRenderer.cpp
 | 
| @@ -13,6 +13,251 @@
 | 
|  #include "GrTBackendProcessorFactory.h"
 | 
|  #include "SkColorPriv.h"
 | 
|  #include "GrGeometryProcessor.h"
 | 
| +
 | 
| +///////////////////////////////////////////////////////////////////////////////
 | 
| +class GrGLAlignedRectEffect;
 | 
| +
 | 
| +// Axis Aligned special case
 | 
| +class GrAlignedRectEffect : public GrGeometryProcessor {
 | 
| +public:
 | 
| +    static GrGeometryProcessor* Create() {
 | 
| +        GR_CREATE_STATIC_PROCESSOR(gAlignedRectEffect, GrAlignedRectEffect, ());
 | 
| +        gAlignedRectEffect->ref();
 | 
| +        return gAlignedRectEffect;
 | 
| +    }
 | 
| +
 | 
| +    virtual ~GrAlignedRectEffect() {}
 | 
| +
 | 
| +    static const char* Name() { return "AlignedRectEdge"; }
 | 
| +
 | 
| +    const GrShaderVar& inRect() const { return fInRect; }
 | 
| +
 | 
| +    virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
 | 
| +        return GrTBackendGeometryProcessorFactory<GrAlignedRectEffect>::getInstance();
 | 
| +    }
 | 
| +
 | 
| +    class GLProcessor : public GrGLGeometryProcessor {
 | 
| +    public:
 | 
| +        GLProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&)
 | 
| +        : INHERITED (factory) {}
 | 
| +
 | 
| +        virtual void emitCode(const EmitArgs& args) SK_OVERRIDE {
 | 
| +            // setup the varying for the Axis aligned rect effect
 | 
| +            //      xy -> interpolated offset
 | 
| +            //      zw -> w/2+0.5, h/2+0.5
 | 
| +            GrGLVertToFrag v(kVec4f_GrSLType);
 | 
| +            args.fPB->addVarying("Rect", &v);
 | 
| +
 | 
| +            const GrShaderVar& inRect = args.fGP.cast<GrAlignedRectEffect>().inRect();
 | 
| +            GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
 | 
| +            vsBuilder->codeAppendf("\t%s = %s;\n", v.fsIn(), inRect.c_str());
 | 
| +
 | 
| +            GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
 | 
| +            // TODO: compute all these offsets, spans, and scales in the VS
 | 
| +            fsBuilder->codeAppendf("\tfloat insetW = min(1.0, %s.z) - 0.5;\n", v.fsIn());
 | 
| +            fsBuilder->codeAppendf("\tfloat insetH = min(1.0, %s.w) - 0.5;\n", v.fsIn());
 | 
| +            fsBuilder->codeAppend("\tfloat outset = 0.5;\n");
 | 
| +            // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
 | 
| +            // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
 | 
| +            fsBuilder->codeAppend("\tfloat spanW = insetW + outset;\n");
 | 
| +            fsBuilder->codeAppend("\tfloat spanH = insetH + outset;\n");
 | 
| +            // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
 | 
| +            // value of coverage that is used. In other words it is the coverage that is
 | 
| +            // used in the interior of the rect after the ramp.
 | 
| +            fsBuilder->codeAppend("\tfloat scaleW = min(1.0, 2.0*insetW/spanW);\n");
 | 
| +            fsBuilder->codeAppend("\tfloat scaleH = min(1.0, 2.0*insetH/spanH);\n");
 | 
| +
 | 
| +            // Compute the coverage for the rect's width
 | 
| +            fsBuilder->codeAppendf(
 | 
| +                "\tfloat coverage = scaleW*clamp((%s.z-abs(%s.x))/spanW, 0.0, 1.0);\n", v.fsIn(),
 | 
| +                v.fsIn());
 | 
| +            // Compute the coverage for the rect's height and merge with the width
 | 
| +            fsBuilder->codeAppendf(
 | 
| +                "\tcoverage = coverage*scaleH*clamp((%s.w-abs(%s.y))/spanH, 0.0, 1.0);\n",
 | 
| +                v.fsIn(), v.fsIn());
 | 
| +
 | 
| +
 | 
| +            fsBuilder->codeAppendf("\t%s = %s;\n", args.fOutput,
 | 
| +                                   (GrGLSLExpr4(args.fInput) * GrGLSLExpr1("coverage")).c_str());
 | 
| +        }
 | 
| +
 | 
| +        static void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*) {}
 | 
| +
 | 
| +        virtual void setData(const GrGLProgramDataManager& pdman, const GrProcessor&) SK_OVERRIDE {}
 | 
| +
 | 
| +    private:
 | 
| +        typedef GrGLGeometryProcessor INHERITED;
 | 
| +    };
 | 
| +
 | 
| +
 | 
| +private:
 | 
| +    GrAlignedRectEffect()
 | 
| +        : fInRect(this->addVertexAttrib(GrShaderVar("inRect",
 | 
| +                                                    kVec4f_GrSLType,
 | 
| +                                                    GrShaderVar::kAttribute_TypeModifier))) {
 | 
| +    }
 | 
| +
 | 
| +    const GrShaderVar& fInRect;
 | 
| +
 | 
| +    virtual bool onIsEqual(const GrGeometryProcessor&) const SK_OVERRIDE { return true; }
 | 
| +
 | 
| +    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
 | 
| +        inout->mulByUnknownAlpha();
 | 
| +    }
 | 
| +
 | 
| +    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
 | 
| +
 | 
| +    typedef GrGeometryProcessor INHERITED;
 | 
| +};
 | 
| +
 | 
| +
 | 
| +GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrAlignedRectEffect);
 | 
| +
 | 
| +GrGeometryProcessor* GrAlignedRectEffect::TestCreate(SkRandom* random,
 | 
| +                                                     GrContext* context,
 | 
| +                                                     const GrDrawTargetCaps&,
 | 
| +                                                     GrTexture* textures[]) {
 | 
| +    return GrAlignedRectEffect::Create();
 | 
| +}
 | 
| +
 | 
| +///////////////////////////////////////////////////////////////////////////////
 | 
| +class GrGLRectEffect;
 | 
| +
 | 
| +/**
 | 
| + * The output of this effect is a modulation of the input color and coverage
 | 
| + * for an arbitrarily oriented rect. The rect is specified as:
 | 
| + *      Center of the rect
 | 
| + *      Unit vector point down the height of the rect
 | 
| + *      Half width + 0.5
 | 
| + *      Half height + 0.5
 | 
| + * The center and vector are stored in a vec4 varying ("RectEdge") with the
 | 
| + * center in the xy components and the vector in the zw components.
 | 
| + * The munged width and height are stored in a vec2 varying ("WidthHeight")
 | 
| + * with the width in x and the height in y.
 | 
| + */
 | 
| +
 | 
| +class GrRectEffect : public GrGeometryProcessor {
 | 
| +public:
 | 
| +    static GrGeometryProcessor* Create() {
 | 
| +        GR_CREATE_STATIC_PROCESSOR(gRectEffect, GrRectEffect, ());
 | 
| +        gRectEffect->ref();
 | 
| +        return gRectEffect;
 | 
| +    }
 | 
| +
 | 
| +    virtual ~GrRectEffect() {}
 | 
| +
 | 
| +    static const char* Name() { return "RectEdge"; }
 | 
| +
 | 
| +    const GrShaderVar& inRectEdge() const { return fInRectEdge; }
 | 
| +    const GrShaderVar& inWidthHeight() const { return fInWidthHeight; }
 | 
| +
 | 
| +    virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
 | 
| +        return GrTBackendGeometryProcessorFactory<GrRectEffect>::getInstance();
 | 
| +    }
 | 
| +
 | 
| +    class GLProcessor : public GrGLGeometryProcessor {
 | 
| +    public:
 | 
| +        GLProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&)
 | 
| +        : INHERITED (factory) {}
 | 
| +
 | 
| +        virtual void emitCode(const EmitArgs& args) SK_OVERRIDE {
 | 
| +            // setup the varying for the center point and the unit vector
 | 
| +            // that points down the height of the rect
 | 
| +            GrGLVertToFrag rectEdge(kVec4f_GrSLType);
 | 
| +            args.fPB->addVarying("RectEdge", &rectEdge);
 | 
| +
 | 
| +            const GrRectEffect& rectEffect = args.fGP.cast<GrRectEffect>();
 | 
| +            GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
 | 
| +            vsBuilder->codeAppendf("%s = %s;", rectEdge.vsOut(), rectEffect.inRectEdge().c_str());
 | 
| +
 | 
| +            // setup the varying for width/2+.5 and height/2+.5
 | 
| +            GrGLVertToFrag widthHeight(kVec2f_GrSLType);
 | 
| +            args.fPB->addVarying("WidthHeight", &widthHeight);
 | 
| +            vsBuilder->codeAppendf("%s = %s;",
 | 
| +                                   widthHeight.vsOut(),
 | 
| +                                   rectEffect.inWidthHeight().c_str());
 | 
| +
 | 
| +            GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
 | 
| +            // TODO: compute all these offsets, spans, and scales in the VS
 | 
| +            fsBuilder->codeAppendf("\tfloat insetW = min(1.0, %s.x) - 0.5;\n", widthHeight.fsIn());
 | 
| +            fsBuilder->codeAppendf("\tfloat insetH = min(1.0, %s.y) - 0.5;\n", widthHeight.fsIn());
 | 
| +            fsBuilder->codeAppend("\tfloat outset = 0.5;\n");
 | 
| +            // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
 | 
| +            // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
 | 
| +            fsBuilder->codeAppend("\tfloat spanW = insetW + outset;\n");
 | 
| +            fsBuilder->codeAppend("\tfloat spanH = insetH + outset;\n");
 | 
| +            // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
 | 
| +            // value of coverage that is used. In other words it is the coverage that is
 | 
| +            // used in the interior of the rect after the ramp.
 | 
| +            fsBuilder->codeAppend("\tfloat scaleW = min(1.0, 2.0*insetW/spanW);\n");
 | 
| +            fsBuilder->codeAppend("\tfloat scaleH = min(1.0, 2.0*insetH/spanH);\n");
 | 
| +
 | 
| +            // Compute the coverage for the rect's width
 | 
| +            fsBuilder->codeAppendf("\tvec2 offset = %s.xy - %s.xy;\n",
 | 
| +                                   fsBuilder->fragmentPosition(), rectEdge.fsIn());
 | 
| +            fsBuilder->codeAppendf("\tfloat perpDot = abs(offset.x * %s.w - offset.y * %s.z);\n",
 | 
| +                                   rectEdge.fsIn(), rectEdge.fsIn());
 | 
| +            fsBuilder->codeAppendf(
 | 
| +                "\tfloat coverage = scaleW*clamp((%s.x-perpDot)/spanW, 0.0, 1.0);\n",
 | 
| +                widthHeight.fsIn());
 | 
| +
 | 
| +            // Compute the coverage for the rect's height and merge with the width
 | 
| +            fsBuilder->codeAppendf("\tperpDot = abs(dot(offset, %s.zw));\n",
 | 
| +                                   rectEdge.fsIn());
 | 
| +            fsBuilder->codeAppendf(
 | 
| +                    "\tcoverage = coverage*scaleH*clamp((%s.y-perpDot)/spanH, 0.0, 1.0);\n",
 | 
| +                    widthHeight.fsIn());
 | 
| +
 | 
| +
 | 
| +            fsBuilder->codeAppendf("\t%s = %s;\n", args.fOutput,
 | 
| +                                   (GrGLSLExpr4(args.fInput) * GrGLSLExpr1("coverage")).c_str());
 | 
| +        }
 | 
| +
 | 
| +        static void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*) {}
 | 
| +
 | 
| +        virtual void setData(const GrGLProgramDataManager& pdman, const GrProcessor&) SK_OVERRIDE {}
 | 
| +
 | 
| +    private:
 | 
| +        typedef GrGLGeometryProcessor INHERITED;
 | 
| +    };
 | 
| +
 | 
| +
 | 
| +
 | 
| +private:
 | 
| +    GrRectEffect()
 | 
| +        : fInRectEdge(this->addVertexAttrib(GrShaderVar("inRectEdge",
 | 
| +                                                        kVec4f_GrSLType,
 | 
| +                                                        GrShaderVar::kAttribute_TypeModifier)))
 | 
| +        , fInWidthHeight(this->addVertexAttrib(
 | 
| +                GrShaderVar("inWidthHeight",
 | 
| +                            kVec2f_GrSLType,
 | 
| +                            GrShaderVar::kAttribute_TypeModifier))) {
 | 
| +        this->setWillReadFragmentPosition();
 | 
| +    }
 | 
| +
 | 
| +    virtual bool onIsEqual(const GrGeometryProcessor&) const SK_OVERRIDE { return true; }
 | 
| +
 | 
| +    virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
 | 
| +        inout->mulByUnknownAlpha();
 | 
| +    }
 | 
| +
 | 
| +    const GrShaderVar& fInRectEdge;
 | 
| +    const GrShaderVar& fInWidthHeight;
 | 
| +
 | 
| +    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
 | 
| +
 | 
| +    typedef GrGeometryProcessor INHERITED;
 | 
| +};
 | 
| +
 | 
| +
 | 
| +GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrRectEffect);
 | 
| +
 | 
| +GrGeometryProcessor* GrRectEffect::TestCreate(SkRandom* random,
 | 
| +                                              GrContext* context,
 | 
| +                                              const GrDrawTargetCaps&,
 | 
| +                                              GrTexture* textures[]) {
 | 
| +    return GrRectEffect::Create();
 | 
| +}
 | 
|  
 | 
|  ///////////////////////////////////////////////////////////////////////////////
 | 
|  
 | 
| @@ -303,6 +548,155 @@
 | 
|      target->resetIndexSource();
 | 
|  }
 | 
|  
 | 
| +namespace {
 | 
| +
 | 
| +// Rotated
 | 
| +struct RectVertex {
 | 
| +    SkPoint fPos;
 | 
| +    SkPoint fCenter;
 | 
| +    SkPoint fDir;
 | 
| +    SkPoint fWidthHeight;
 | 
| +};
 | 
| +
 | 
| +// Rotated
 | 
| +extern const GrVertexAttrib gAARectVertexAttribs[] = {
 | 
| +    { kVec2f_GrVertexAttribType, 0,                 kPosition_GrVertexAttribBinding },
 | 
| +    { kVec4f_GrVertexAttribType, sizeof(SkPoint),   kGeometryProcessor_GrVertexAttribBinding },
 | 
| +    { kVec2f_GrVertexAttribType, 3*sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding }
 | 
| +};
 | 
| +
 | 
| +// Axis Aligned
 | 
| +struct AARectVertex {
 | 
| +    SkPoint fPos;
 | 
| +    SkPoint fOffset;
 | 
| +    SkPoint fWidthHeight;
 | 
| +};
 | 
| +
 | 
| +// Axis Aligned
 | 
| +extern const GrVertexAttrib gAAAARectVertexAttribs[] = {
 | 
| +    { kVec2f_GrVertexAttribType, 0,                 kPosition_GrVertexAttribBinding },
 | 
| +    { kVec4f_GrVertexAttribType, sizeof(SkPoint),   kGeometryProcessor_GrVertexAttribBinding },
 | 
| +};
 | 
| +
 | 
| +};
 | 
| +
 | 
| +void GrAARectRenderer::shaderFillAARect(GrDrawTarget* target,
 | 
| +                                        const SkRect& rect,
 | 
| +                                        const SkMatrix& combinedMatrix) {
 | 
| +    GrDrawState* drawState = target->drawState();
 | 
| +
 | 
| +    SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY());
 | 
| +    combinedMatrix.mapPoints(¢er, 1);
 | 
| +
 | 
| +    // compute transformed (0, 1) vector
 | 
| +    SkVector dir = { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] };
 | 
| +    dir.normalize();
 | 
| +
 | 
| +    // compute transformed (width, 0) and (0, height) vectors
 | 
| +    SkVector vec[2] = {
 | 
| +      { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] },
 | 
| +      { combinedMatrix[SkMatrix::kMSkewX],  combinedMatrix[SkMatrix::kMScaleY] }
 | 
| +    };
 | 
| +
 | 
| +    SkScalar newWidth = SkScalarHalf(rect.width() * vec[0].length()) + SK_ScalarHalf;
 | 
| +    SkScalar newHeight = SkScalarHalf(rect.height() * vec[1].length()) + SK_ScalarHalf;
 | 
| +    drawState->setVertexAttribs<gAARectVertexAttribs>(SK_ARRAY_COUNT(gAARectVertexAttribs),
 | 
| +                                                      sizeof(RectVertex));
 | 
| +
 | 
| +    GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
 | 
| +    if (!geo.succeeded()) {
 | 
| +        SkDebugf("Failed to get space for vertices!\n");
 | 
| +        return;
 | 
| +    }
 | 
| +
 | 
| +    RectVertex* verts = reinterpret_cast<RectVertex*>(geo.vertices());
 | 
| +
 | 
| +    GrGeometryProcessor* gp = GrRectEffect::Create();
 | 
| +    drawState->setGeometryProcessor(gp)->unref();
 | 
| +
 | 
| +    for (int i = 0; i < 4; ++i) {
 | 
| +        verts[i].fCenter = center;
 | 
| +        verts[i].fDir = dir;
 | 
| +        verts[i].fWidthHeight.fX = newWidth;
 | 
| +        verts[i].fWidthHeight.fY = newHeight;
 | 
| +    }
 | 
| +
 | 
| +    SkRect devRect;
 | 
| +    combinedMatrix.mapRect(&devRect, rect);
 | 
| +
 | 
| +    SkRect devBounds = {
 | 
| +        devRect.fLeft   - SK_ScalarHalf,
 | 
| +        devRect.fTop    - SK_ScalarHalf,
 | 
| +        devRect.fRight  + SK_ScalarHalf,
 | 
| +        devRect.fBottom + SK_ScalarHalf
 | 
| +    };
 | 
| +
 | 
| +    verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop);
 | 
| +    verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom);
 | 
| +    verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom);
 | 
| +    verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop);
 | 
| +
 | 
| +    target->setIndexSourceToBuffer(fGpu->getContext()->getQuadIndexBuffer());
 | 
| +    target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6);
 | 
| +    target->resetIndexSource();
 | 
| +}
 | 
| +
 | 
| +void GrAARectRenderer::shaderFillAlignedAARect(GrDrawTarget* target,
 | 
| +                                               const SkRect& rect,
 | 
| +                                               const SkMatrix& combinedMatrix) {
 | 
| +    GrDrawState* drawState = target->drawState();
 | 
| +    SkASSERT(combinedMatrix.rectStaysRect());
 | 
| +
 | 
| +    drawState->setVertexAttribs<gAAAARectVertexAttribs>(SK_ARRAY_COUNT(gAAAARectVertexAttribs),
 | 
| +                                                        sizeof(AARectVertex));
 | 
| +
 | 
| +    GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
 | 
| +    if (!geo.succeeded()) {
 | 
| +        SkDebugf("Failed to get space for vertices!\n");
 | 
| +        return;
 | 
| +    }
 | 
| +
 | 
| +    AARectVertex* verts = reinterpret_cast<AARectVertex*>(geo.vertices());
 | 
| +
 | 
| +    GrGeometryProcessor* gp = GrAlignedRectEffect::Create();
 | 
| +    drawState->setGeometryProcessor(gp)->unref();
 | 
| +
 | 
| +    SkRect devRect;
 | 
| +    combinedMatrix.mapRect(&devRect, rect);
 | 
| +
 | 
| +    SkRect devBounds = {
 | 
| +        devRect.fLeft   - SK_ScalarHalf,
 | 
| +        devRect.fTop    - SK_ScalarHalf,
 | 
| +        devRect.fRight  + SK_ScalarHalf,
 | 
| +        devRect.fBottom + SK_ScalarHalf
 | 
| +    };
 | 
| +
 | 
| +    SkPoint widthHeight = {
 | 
| +        SkScalarHalf(devRect.width()) + SK_ScalarHalf,
 | 
| +        SkScalarHalf(devRect.height()) + SK_ScalarHalf
 | 
| +    };
 | 
| +
 | 
| +    verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop);
 | 
| +    verts[0].fOffset = SkPoint::Make(-widthHeight.fX, -widthHeight.fY);
 | 
| +    verts[0].fWidthHeight = widthHeight;
 | 
| +
 | 
| +    verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom);
 | 
| +    verts[1].fOffset = SkPoint::Make(-widthHeight.fX, widthHeight.fY);
 | 
| +    verts[1].fWidthHeight = widthHeight;
 | 
| +
 | 
| +    verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom);
 | 
| +    verts[2].fOffset = widthHeight;
 | 
| +    verts[2].fWidthHeight = widthHeight;
 | 
| +
 | 
| +    verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop);
 | 
| +    verts[3].fOffset = SkPoint::Make(widthHeight.fX, -widthHeight.fY);
 | 
| +    verts[3].fWidthHeight = widthHeight;
 | 
| +
 | 
| +    target->setIndexSourceToBuffer(fGpu->getContext()->getQuadIndexBuffer());
 | 
| +    target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6);
 | 
| +    target->resetIndexSource();
 | 
| +}
 | 
| +
 | 
|  void GrAARectRenderer::strokeAARect(GrDrawTarget* target,
 | 
|                                      const SkRect& rect,
 | 
|                                      const SkMatrix& combinedMatrix,
 | 
| 
 |