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 "GrEffect.h"
     11 #include "gl/GrGLEffect.h"
     12 #include "gl/GrGLSL.h"
     13 #include "GrTBackendEffectFactory.h"
     14 
     15 #include "GrDrawState.h"
     16 #include "GrDrawTarget.h"
     17 #include "GrGpu.h"
     18 
     19 #include "SkRRect.h"
     20 #include "SkStrokeRec.h"
     21 
     22 SK_DEFINE_INST_COUNT(GrOvalRenderer)
     23 
     24 namespace {
     25 
     26 struct CircleVertex {
     27     GrPoint  fPos;
     28     GrPoint  fOffset;
     29     SkScalar fOuterRadius;
     30     SkScalar fInnerRadius;
     31 };
     32 
     33 struct EllipseVertex {
     34     GrPoint  fPos;
     35     GrPoint  fOffset;
     36     GrPoint  fOuterRadii;
     37     GrPoint  fInnerRadii;
     38 };
     39 
     40 inline bool circle_stays_circle(const SkMatrix& m) {
     41     return m.isSimilarity();
     42 }
     43 
     44 }
     45 
     46 ///////////////////////////////////////////////////////////////////////////////
     47 
     48 /**
     49  * The output of this effect is a modulation of the input color and coverage for a circle,
     50  * specified as offset_x, offset_y (both from center point), outer radius and inner radius.
     51  */
     52 
     53 class CircleEdgeEffect : public GrEffect {
     54 public:
     55     static GrEffectRef* Create(bool stroke) {
     56         GR_CREATE_STATIC_EFFECT(gCircleStrokeEdge, CircleEdgeEffect, (true));
     57         GR_CREATE_STATIC_EFFECT(gCircleFillEdge, CircleEdgeEffect, (false));
     58 
     59         if (stroke) {
     60             gCircleStrokeEdge->ref();
     61             return gCircleStrokeEdge;
     62         } else {
     63             gCircleFillEdge->ref();
     64             return gCircleFillEdge;
     65         }
     66     }
     67 
     68     virtual void getConstantColorComponents(GrColor* color,
     69                                             uint32_t* validFlags) const SK_OVERRIDE {
     70         *validFlags = 0;
     71     }
     72 
     73     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
     74         return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance();
     75     }
     76 
     77     virtual ~CircleEdgeEffect() {}
     78 
     79     static const char* Name() { return "CircleEdge"; }
     80 
     81     inline bool isStroked() const { return fStroke; }
     82 
     83     class GLEffect : public GrGLEffect {
     84     public:
     85         GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
     86         : INHERITED (factory) {}
     87 
     88         virtual void emitCode(GrGLShaderBuilder* builder,
     89                               const GrDrawEffect& drawEffect,
     90                               EffectKey key,
     91                               const char* outputColor,
     92                               const char* inputColor,
     93                               const TextureSamplerArray& samplers) SK_OVERRIDE {
     94             const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
     95             const char *vsName, *fsName;
     96             builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName);
     97 
     98             const SkString* attrName =
     99                 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
    100             builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
    101 
    102             builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName);
    103             builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName);
    104             if (circleEffect.isStroked()) {
    105                 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName);
    106                 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
    107             }
    108 
    109             SkString modulate;
    110             GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
    111             builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
    112         }
    113 
    114         static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
    115             const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
    116 
    117             return circleEffect.isStroked() ? 0x1 : 0x0;
    118         }
    119 
    120         virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
    121 
    122     private:
    123         typedef GrGLEffect INHERITED;
    124     };
    125 
    126 
    127 private:
    128     CircleEdgeEffect(bool stroke) : GrEffect() {
    129         this->addVertexAttrib(kVec4f_GrSLType);
    130         fStroke = stroke;
    131     }
    132 
    133     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
    134         const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other);
    135         return cee.fStroke == fStroke;
    136     }
    137 
    138     bool fStroke;
    139 
    140     GR_DECLARE_EFFECT_TEST;
    141 
    142     typedef GrEffect INHERITED;
    143 };
    144 
    145 GR_DEFINE_EFFECT_TEST(CircleEdgeEffect);
    146 
    147 GrEffectRef* CircleEdgeEffect::TestCreate(SkMWCRandom* random,
    148                                           GrContext* context,
    149                                           const GrDrawTargetCaps&,
    150                                           GrTexture* textures[]) {
    151     return CircleEdgeEffect::Create(random->nextBool());
    152 }
    153 
    154 ///////////////////////////////////////////////////////////////////////////////
    155 
    156 /**
    157  * The output of this effect is a modulation of the input color and coverage for an axis-aligned
    158  * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
    159  * in both x and y directions.
    160  *
    161  * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
    162  */
    163 
    164 class EllipseEdgeEffect : public GrEffect {
    165 public:
    166     static GrEffectRef* Create(bool stroke) {
    167         GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, EllipseEdgeEffect, (true));
    168         GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, EllipseEdgeEffect, (false));
    169 
    170         if (stroke) {
    171             gEllipseStrokeEdge->ref();
    172             return gEllipseStrokeEdge;
    173         } else {
    174             gEllipseFillEdge->ref();
    175             return gEllipseFillEdge;
    176         }
    177     }
    178 
    179     virtual void getConstantColorComponents(GrColor* color,
    180                                             uint32_t* validFlags) const SK_OVERRIDE {
    181         *validFlags = 0;
    182     }
    183 
    184     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
    185         return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance();
    186     }
    187 
    188     virtual ~EllipseEdgeEffect() {}
    189 
    190     static const char* Name() { return "EllipseEdge"; }
    191 
    192     inline bool isStroked() const { return fStroke; }
    193 
    194     class GLEffect : public GrGLEffect {
    195     public:
    196         GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
    197         : INHERITED (factory) {}
    198 
    199         virtual void emitCode(GrGLShaderBuilder* builder,
    200                               const GrDrawEffect& drawEffect,
    201                               EffectKey key,
    202                               const char* outputColor,
    203                               const char* inputColor,
    204                               const TextureSamplerArray& samplers) SK_OVERRIDE {
    205             const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
    206 
    207             const char *vsOffsetName, *fsOffsetName;
    208             const char *vsRadiiName, *fsRadiiName;
    209 
    210             builder->addVarying(kVec2f_GrSLType, "EllipseOffsets", &vsOffsetName, &fsOffsetName);
    211             const SkString* attr0Name =
    212                 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
    213             builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName, attr0Name->c_str());
    214 
    215             builder->addVarying(kVec4f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName);
    216             const SkString* attr1Name =
    217                 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
    218             builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr1Name->c_str());
    219 
    220             // for outer curve
    221             builder->fsCodeAppendf("\tvec2 scaledOffset = %s*%s.xy;\n", fsOffsetName, fsRadiiName);
    222             builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n");
    223             builder->fsCodeAppendf("\tvec2 grad = 2.0*scaledOffset*%s.xy;\n", fsRadiiName);
    224             builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
    225             // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
    226             // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
    227             // TODO: restrict this to Adreno-only
    228             builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
    229             builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
    230             builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
    231 
    232             // for inner curve
    233             if (ellipseEffect.isStroked()) {
    234                 builder->fsCodeAppendf("\tscaledOffset = %s*%s.zw;\n", fsOffsetName, fsRadiiName);
    235                 builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n");
    236                 builder->fsCodeAppendf("\tgrad = 2.0*scaledOffset*%s.zw;\n", fsRadiiName);
    237                 builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n");
    238                 builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n");
    239             }
    240 
    241             SkString modulate;
    242             GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
    243             builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
    244         }
    245 
    246         static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
    247             const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
    248 
    249             return ellipseEffect.isStroked() ? 0x1 : 0x0;
    250         }
    251 
    252         virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
    253         }
    254 
    255     private:
    256         typedef GrGLEffect INHERITED;
    257     };
    258 
    259 private:
    260     EllipseEdgeEffect(bool stroke) : GrEffect() {
    261         this->addVertexAttrib(kVec2f_GrSLType);
    262         this->addVertexAttrib(kVec4f_GrSLType);
    263         fStroke = stroke;
    264     }
    265 
    266     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
    267         const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other);
    268         return eee.fStroke == fStroke;
    269     }
    270 
    271     bool fStroke;
    272 
    273     GR_DECLARE_EFFECT_TEST;
    274 
    275     typedef GrEffect INHERITED;
    276 };
    277 
    278 GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect);
    279 
    280 GrEffectRef* EllipseEdgeEffect::TestCreate(SkMWCRandom* random,
    281                                            GrContext* context,
    282                                            const GrDrawTargetCaps&,
    283                                            GrTexture* textures[]) {
    284     return EllipseEdgeEffect::Create(random->nextBool());
    285 }
    286 
    287 ///////////////////////////////////////////////////////////////////////////////
    288 
    289 void GrOvalRenderer::reset() {
    290     GrSafeSetNull(fRRectIndexBuffer);
    291 }
    292 
    293 bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bool useAA,
    294                               const SkRect& oval, const SkStrokeRec& stroke)
    295 {
    296     if (!useAA) {
    297         return false;
    298     }
    299 
    300     const SkMatrix& vm = context->getMatrix();
    301 
    302     // we can draw circles
    303     if (SkScalarNearlyEqual(oval.width(), oval.height())
    304         && circle_stays_circle(vm)) {
    305         this->drawCircle(target, useAA, oval, stroke);
    306 
    307     // and axis-aligned ellipses only
    308     } else if (vm.rectStaysRect()) {
    309         return this->drawEllipse(target, useAA, oval, stroke);
    310 
    311     } else {
    312         return false;
    313     }
    314 
    315     return true;
    316 }
    317 
    318 namespace {
    319 
    320 ///////////////////////////////////////////////////////////////////////////////
    321 
    322 // position + edge
    323 extern const GrVertexAttrib gCircleVertexAttribs[] = {
    324     {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
    325     {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
    326 };
    327 
    328 };
    329 
    330 void GrOvalRenderer::drawCircle(GrDrawTarget* target,
    331                                 bool useAA,
    332                                 const SkRect& circle,
    333                                 const SkStrokeRec& stroke)
    334 {
    335     GrDrawState* drawState = target->drawState();
    336 
    337     const SkMatrix& vm = drawState->getViewMatrix();
    338     GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
    339     vm.mapPoints(&center, 1);
    340     SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
    341     SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
    342 
    343     GrDrawState::AutoViewMatrixRestore avmr;
    344     if (!avmr.setIdentity(drawState)) {
    345         return;
    346     }
    347 
    348     drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
    349     GrAssert(sizeof(CircleVertex) == drawState->getVertexSize());
    350 
    351     GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
    352     if (!geo.succeeded()) {
    353         GrPrintf("Failed to get space for vertices!\n");
    354         return;
    355     }
    356 
    357     CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
    358 
    359     SkStrokeRec::Style style = stroke.getStyle();
    360     bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
    361 
    362     GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
    363     static const int kCircleEdgeAttrIndex = 1;
    364     drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
    365 
    366     SkScalar innerRadius = 0.0f;
    367     SkScalar outerRadius = radius;
    368     SkScalar halfWidth = 0;
    369     if (style != SkStrokeRec::kFill_Style) {
    370         if (SkScalarNearlyZero(strokeWidth)) {
    371             halfWidth = SK_ScalarHalf;
    372         } else {
    373             halfWidth = SkScalarHalf(strokeWidth);
    374         }
    375 
    376         outerRadius += halfWidth;
    377         if (isStroked) {
    378             innerRadius = radius - halfWidth;
    379             isStroked = (innerRadius > 0);
    380         }
    381     }
    382 
    383     // The radii are outset for two reasons. First, it allows the shader to simply perform
    384     // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
    385     // verts of the bounding box that is rendered and the outset ensures the box will cover all
    386     // pixels partially covered by the circle.
    387     outerRadius += SK_ScalarHalf;
    388     innerRadius -= SK_ScalarHalf;
    389 
    390     SkRect bounds = SkRect::MakeLTRB(
    391         center.fX - outerRadius,
    392         center.fY - outerRadius,
    393         center.fX + outerRadius,
    394         center.fY + outerRadius
    395     );
    396 
    397     verts[0].fPos = SkPoint::Make(bounds.fLeft,  bounds.fTop);
    398     verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius);
    399     verts[0].fOuterRadius = outerRadius;
    400     verts[0].fInnerRadius = innerRadius;
    401 
    402     verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
    403     verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius);
    404     verts[1].fOuterRadius = outerRadius;
    405     verts[1].fInnerRadius = innerRadius;
    406 
    407     verts[2].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
    408     verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius);
    409     verts[2].fOuterRadius = outerRadius;
    410     verts[2].fInnerRadius = innerRadius;
    411 
    412     verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
    413     verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius);
    414     verts[3].fOuterRadius = outerRadius;
    415     verts[3].fInnerRadius = innerRadius;
    416 
    417     target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
    418 }
    419 
    420 ///////////////////////////////////////////////////////////////////////////////
    421 
    422 namespace {
    423 
    424 // position + edge
    425 extern const GrVertexAttrib gEllipseVertexAttribs[] = {
    426     {kVec2f_GrVertexAttribType, 0,                 kPosition_GrVertexAttribBinding},
    427     {kVec2f_GrVertexAttribType, sizeof(GrPoint),   kEffect_GrVertexAttribBinding},
    428     {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
    429 };
    430 
    431 };
    432 
    433 bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
    434                                  bool useAA,
    435                                  const SkRect& ellipse,
    436                                  const SkStrokeRec& stroke)
    437 {
    438     GrDrawState* drawState = target->drawState();
    439 #ifdef SK_DEBUG
    440     {
    441         // we should have checked for this previously
    442         bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
    443         SkASSERT(useAA && isAxisAlignedEllipse);
    444     }
    445 #endif
    446 
    447     // do any matrix crunching before we reset the draw state for device coords
    448     const SkMatrix& vm = drawState->getViewMatrix();
    449     GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
    450     vm.mapPoints(&center, 1);
    451     SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
    452     SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
    453     SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius +
    454                                    vm[SkMatrix::kMSkewY]*ellipseYRadius);
    455     SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius +
    456                                    vm[SkMatrix::kMScaleY]*ellipseYRadius);
    457 
    458     // do (potentially) anisotropic mapping of stroke
    459     SkVector scaledStroke;
    460     SkScalar strokeWidth = stroke.getWidth();
    461     scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
    462     scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
    463 
    464     SkStrokeRec::Style style = stroke.getStyle();
    465     bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
    466 
    467     SkScalar innerXRadius = 0.0f;
    468     SkScalar innerYRadius = 0.0f;
    469     if (SkStrokeRec::kFill_Style != style) {
    470         if (SkScalarNearlyZero(scaledStroke.length())) {
    471             scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
    472         } else {
    473             scaledStroke.scale(SK_ScalarHalf);
    474         }
    475 
    476         // we only handle thick strokes for near-circular ellipses
    477         if (scaledStroke.length() > SK_ScalarHalf &&
    478             (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
    479             return false;
    480         }
    481 
    482         // we don't handle it if curvature of the stroke is less than curvature of the ellipse
    483         if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
    484             scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
    485             return false;
    486         }
    487 
    488         // this is legit only if scale & translation (which should be the case at the moment)
    489         if (isStroked) {
    490             innerXRadius = xRadius - scaledStroke.fX;
    491             innerYRadius = yRadius - scaledStroke.fY;
    492             isStroked = (innerXRadius > 0 && innerYRadius > 0);
    493         }
    494 
    495         xRadius += scaledStroke.fX;
    496         yRadius += scaledStroke.fY;
    497     }
    498 
    499     GrDrawState::AutoViewMatrixRestore avmr;
    500     if (!avmr.setIdentity(drawState)) {
    501         return false;
    502     }
    503 
    504     drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
    505     GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize());
    506 
    507     GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
    508     if (!geo.succeeded()) {
    509         GrPrintf("Failed to get space for vertices!\n");
    510         return false;
    511     }
    512 
    513     EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
    514 
    515     GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
    516 
    517     static const int kEllipseCenterAttrIndex = 1;
    518     static const int kEllipseEdgeAttrIndex = 2;
    519     drawState->addCoverageEffect(effect, kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
    520 
    521     // Compute the reciprocals of the radii here to save time in the shader
    522     SkScalar xRadRecip = SkScalarInvert(xRadius);
    523     SkScalar yRadRecip = SkScalarInvert(yRadius);
    524     SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
    525     SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
    526 
    527     // We've extended the outer x radius out half a pixel to antialias.
    528     // This will also expand the rect so all the pixels will be captured.
    529     // TODO: Consider if we should use sqrt(2)/2 instead
    530     xRadius += SK_ScalarHalf;
    531     yRadius += SK_ScalarHalf;
    532 
    533     SkRect bounds = SkRect::MakeLTRB(
    534         center.fX - xRadius,
    535         center.fY - yRadius,
    536         center.fX + xRadius,
    537         center.fY + yRadius
    538     );
    539 
    540     verts[0].fPos = SkPoint::Make(bounds.fLeft,  bounds.fTop);
    541     verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
    542     verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    543     verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    544 
    545     verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
    546     verts[1].fOffset = SkPoint::Make(xRadius, -yRadius);
    547     verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    548     verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    549 
    550     verts[2].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
    551     verts[2].fOffset = SkPoint::Make(-xRadius, yRadius);
    552     verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    553     verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    554 
    555     verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
    556     verts[3].fOffset = SkPoint::Make(xRadius, yRadius);
    557     verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    558     verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    559 
    560     target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
    561 
    562     return true;
    563 }
    564 
    565 ///////////////////////////////////////////////////////////////////////////////
    566 
    567 static const uint16_t gRRectIndices[] = {
    568     // corners
    569     0, 1, 5, 0, 5, 4,
    570     2, 3, 7, 2, 7, 6,
    571     8, 9, 13, 8, 13, 12,
    572     10, 11, 15, 10, 15, 14,
    573 
    574     // edges
    575     1, 2, 6, 1, 6, 5,
    576     4, 5, 9, 4, 9, 8,
    577     6, 7, 11, 6, 11, 10,
    578     9, 10, 14, 9, 14, 13,
    579 
    580     // center
    581     // we place this at the end so that we can ignore these indices when rendering stroke-only
    582     5, 6, 10, 5, 10, 9
    583 };
    584 
    585 
    586 GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) {
    587     if (NULL == fRRectIndexBuffer) {
    588         fRRectIndexBuffer =
    589         gpu->createIndexBuffer(sizeof(gRRectIndices), false);
    590         if (NULL != fRRectIndexBuffer) {
    591 #if GR_DEBUG
    592             bool updated =
    593 #endif
    594             fRRectIndexBuffer->updateData(gRRectIndices,
    595                                           sizeof(gRRectIndices));
    596             GR_DEBUGASSERT(updated);
    597         }
    598     }
    599     return fRRectIndexBuffer;
    600 }
    601 
    602 bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA,
    603                                      const SkRRect& rrect, const SkStrokeRec& stroke)
    604 {
    605     // only anti-aliased rrects for now
    606     if (!useAA) {
    607         return false;
    608     }
    609 
    610     const SkMatrix& vm = context->getMatrix();
    611 #ifdef SK_DEBUG
    612     {
    613         // we should have checked for this previously
    614         SkASSERT(useAA && vm.rectStaysRect() && rrect.isSimple());
    615     }
    616 #endif
    617 
    618     // do any matrix crunching before we reset the draw state for device coords
    619     const SkRect& rrectBounds = rrect.getBounds();
    620     SkRect bounds;
    621     vm.mapRect(&bounds, rrectBounds);
    622 
    623     SkVector radii = rrect.getSimpleRadii();
    624     SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*radii.fX +
    625                                    vm[SkMatrix::kMSkewY]*radii.fY);
    626     SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX +
    627                                    vm[SkMatrix::kMScaleY]*radii.fY);
    628 
    629     // if hairline stroke is greater than radius, we don't handle that right now
    630     SkStrokeRec::Style style = stroke.getStyle();
    631     if (SkStrokeRec::kHairline_Style == style &&
    632         (SK_ScalarHalf >= xRadius || SK_ScalarHalf >= yRadius)) {
    633         return false;
    634     }
    635 
    636     // do (potentially) anisotropic mapping of stroke
    637     SkVector scaledStroke;
    638     SkScalar strokeWidth = stroke.getWidth();
    639     scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
    640     scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
    641 
    642     // if half of strokewidth is greater than radius, we don't handle that right now
    643     if (SK_ScalarHalf*scaledStroke.fX >= xRadius || SK_ScalarHalf*scaledStroke.fY >= yRadius) {
    644         return false;
    645     }
    646 
    647     // reset to device coordinates
    648     GrDrawState* drawState = target->drawState();
    649     GrDrawState::AutoViewMatrixRestore avmr;
    650     if (!avmr.setIdentity(drawState)) {
    651         return false;
    652     }
    653 
    654     bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
    655 
    656     GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu());
    657     if (NULL == indexBuffer) {
    658         GrPrintf("Failed to create index buffer!\n");
    659         return false;
    660     }
    661 
    662     // if the corners are circles, use the circle renderer
    663     if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
    664         drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
    665         GrAssert(sizeof(CircleVertex) == drawState->getVertexSize());
    666 
    667         GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
    668         if (!geo.succeeded()) {
    669             GrPrintf("Failed to get space for vertices!\n");
    670             return false;
    671         }
    672         CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
    673 
    674         SkScalar innerRadius = 0.0f;
    675         SkScalar outerRadius = xRadius;
    676         SkScalar halfWidth = 0;
    677         if (style != SkStrokeRec::kFill_Style) {
    678             if (SkScalarNearlyZero(scaledStroke.fX)) {
    679                 halfWidth = SK_ScalarHalf;
    680             } else {
    681                 halfWidth = SkScalarHalf(scaledStroke.fX);
    682             }
    683 
    684             if (isStroked) {
    685                 innerRadius = xRadius - halfWidth;
    686                 isStroked = (innerRadius > 0);
    687             }
    688             outerRadius += halfWidth;
    689             bounds.outset(halfWidth, halfWidth);
    690         }
    691 
    692         GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
    693         static const int kCircleEdgeAttrIndex = 1;
    694         drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
    695 
    696         // The radii are outset for two reasons. First, it allows the shader to simply perform
    697         // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
    698         // verts of the bounding box that is rendered and the outset ensures the box will cover all
    699         // pixels partially covered by the circle.
    700         outerRadius += SK_ScalarHalf;
    701         innerRadius -= SK_ScalarHalf;
    702 
    703         // Expand the rect so all the pixels will be captured.
    704         bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
    705 
    706         SkScalar yCoords[4] = {
    707             bounds.fTop,
    708             bounds.fTop + outerRadius,
    709             bounds.fBottom - outerRadius,
    710             bounds.fBottom
    711         };
    712         SkScalar yOuterRadii[4] = {
    713             -outerRadius,
    714             0,
    715             0,
    716             outerRadius
    717         };
    718         for (int i = 0; i < 4; ++i) {
    719             verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
    720             verts->fOffset = SkPoint::Make(-outerRadius, yOuterRadii[i]);
    721             verts->fOuterRadius = outerRadius;
    722             verts->fInnerRadius = innerRadius;
    723             verts++;
    724 
    725             verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
    726             verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
    727             verts->fOuterRadius = outerRadius;
    728             verts->fInnerRadius = innerRadius;
    729             verts++;
    730 
    731             verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
    732             verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
    733             verts->fOuterRadius = outerRadius;
    734             verts->fInnerRadius = innerRadius;
    735             verts++;
    736 
    737             verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
    738             verts->fOffset = SkPoint::Make(outerRadius, yOuterRadii[i]);
    739             verts->fOuterRadius = outerRadius;
    740             verts->fInnerRadius = innerRadius;
    741             verts++;
    742         }
    743 
    744         // drop out the middle quad if we're stroked
    745         int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
    746         target->setIndexSourceToBuffer(indexBuffer);
    747         target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
    748 
    749     // otherwise we use the ellipse renderer
    750     } else {
    751         drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
    752         GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize());
    753 
    754         SkScalar innerXRadius = 0.0f;
    755         SkScalar innerYRadius = 0.0f;
    756         if (SkStrokeRec::kFill_Style != style) {
    757             if (SkScalarNearlyZero(scaledStroke.length())) {
    758                 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
    759             } else {
    760                 scaledStroke.scale(SK_ScalarHalf);
    761             }
    762 
    763             // we only handle thick strokes for near-circular ellipses
    764             if (scaledStroke.length() > SK_ScalarHalf &&
    765                 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
    766                 return false;
    767             }
    768 
    769             // we don't handle it if curvature of the stroke is less than curvature of the ellipse
    770             if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
    771                 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
    772                 return false;
    773             }
    774 
    775             // this is legit only if scale & translation (which should be the case at the moment)
    776             if (isStroked) {
    777                 innerXRadius = xRadius - scaledStroke.fX;
    778                 innerYRadius = yRadius - scaledStroke.fY;
    779                 isStroked = (innerXRadius > 0 && innerYRadius > 0);
    780             }
    781 
    782             xRadius += scaledStroke.fX;
    783             yRadius += scaledStroke.fY;
    784             bounds.outset(scaledStroke.fX, scaledStroke.fY);
    785         }
    786 
    787         GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
    788         if (!geo.succeeded()) {
    789             GrPrintf("Failed to get space for vertices!\n");
    790             return false;
    791         }
    792         EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
    793 
    794         GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
    795         static const int kEllipseOffsetAttrIndex = 1;
    796         static const int kEllipseRadiiAttrIndex = 2;
    797         drawState->addCoverageEffect(effect,
    798                                      kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref();
    799 
    800         // Compute the reciprocals of the radii here to save time in the shader
    801         SkScalar xRadRecip = SkScalarInvert(xRadius);
    802         SkScalar yRadRecip = SkScalarInvert(yRadius);
    803         SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
    804         SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
    805 
    806         // Extend the radii out half a pixel to antialias.
    807         SkScalar xOuterRadius = xRadius + SK_ScalarHalf;
    808         SkScalar yOuterRadius = yRadius + SK_ScalarHalf;
    809 
    810         // Expand the rect so all the pixels will be captured.
    811         bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
    812 
    813         SkScalar yCoords[4] = {
    814             bounds.fTop,
    815             bounds.fTop + yOuterRadius,
    816             bounds.fBottom - yOuterRadius,
    817             bounds.fBottom
    818         };
    819         SkScalar yOuterOffsets[4] = {
    820             yOuterRadius,
    821             SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0
    822             SK_ScalarNearlyZero,
    823             yOuterRadius
    824         };
    825 
    826         for (int i = 0; i < 4; ++i) {
    827             verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
    828             verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
    829             verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    830             verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    831             verts++;
    832 
    833             verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
    834             verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
    835             verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    836             verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    837             verts++;
    838 
    839             verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
    840             verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
    841             verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    842             verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    843             verts++;
    844 
    845             verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
    846             verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
    847             verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
    848             verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
    849             verts++;
    850         }
    851 
    852         // drop out the middle quad if we're stroked
    853         int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
    854         target->setIndexSourceToBuffer(indexBuffer);
    855         target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
    856     }
    857 
    858     return true;
    859 }
    860