Home | History | Annotate | Download | only in gpu
      1 
      2 /*
      3  * Copyright 2012 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 #include "GrAAConvexPathRenderer.h"
     10 
     11 #include "GrContext.h"
     12 #include "GrDrawState.h"
     13 #include "GrDrawTargetCaps.h"
     14 #include "GrEffect.h"
     15 #include "GrPathUtils.h"
     16 #include "GrTBackendEffectFactory.h"
     17 #include "SkString.h"
     18 #include "SkStrokeRec.h"
     19 #include "SkTrace.h"
     20 
     21 #include "gl/GrGLEffect.h"
     22 #include "gl/GrGLSL.h"
     23 #include "gl/GrGLVertexEffect.h"
     24 
     25 #include "effects/GrVertexEffect.h"
     26 
     27 GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
     28 }
     29 
     30 struct Segment {
     31     enum {
     32         // These enum values are assumed in member functions below.
     33         kLine = 0,
     34         kQuad = 1,
     35     } fType;
     36 
     37     // line uses one pt, quad uses 2 pts
     38     GrPoint fPts[2];
     39     // normal to edge ending at each pt
     40     GrVec fNorms[2];
     41     // is the corner where the previous segment meets this segment
     42     // sharp. If so, fMid is a normalized bisector facing outward.
     43     GrVec fMid;
     44 
     45     int countPoints() {
     46         GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
     47         return fType + 1;
     48     }
     49     const SkPoint& endPt() const {
     50         GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
     51         return fPts[fType];
     52     };
     53     const SkPoint& endNorm() const {
     54         GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
     55         return fNorms[fType];
     56     };
     57 };
     58 
     59 typedef SkTArray<Segment, true> SegmentArray;
     60 
     61 static void center_of_mass(const SegmentArray& segments, SkPoint* c) {
     62     SkScalar area = 0;
     63     SkPoint center = {0, 0};
     64     int count = segments.count();
     65     SkPoint p0 = {0, 0};
     66     if (count > 2) {
     67         // We translate the polygon so that the first point is at the origin.
     68         // This avoids some precision issues with small area polygons far away
     69         // from the origin.
     70         p0 = segments[0].endPt();
     71         SkPoint pi;
     72         SkPoint pj;
     73         // the first and last iteration of the below loop would compute
     74         // zeros since the starting / ending point is (0,0). So instead we start
     75         // at i=1 and make the last iteration i=count-2.
     76         pj = segments[1].endPt() - p0;
     77         for (int i = 1; i < count - 1; ++i) {
     78             pi = pj;
     79             const SkPoint pj = segments[i + 1].endPt() - p0;
     80 
     81             SkScalar t = SkScalarMul(pi.fX, pj.fY) - SkScalarMul(pj.fX, pi.fY);
     82             area += t;
     83             center.fX += (pi.fX + pj.fX) * t;
     84             center.fY += (pi.fY + pj.fY) * t;
     85 
     86         }
     87     }
     88     // If the poly has no area then we instead return the average of
     89     // its points.
     90     if (SkScalarNearlyZero(area)) {
     91         SkPoint avg;
     92         avg.set(0, 0);
     93         for (int i = 0; i < count; ++i) {
     94             const SkPoint& pt = segments[i].endPt();
     95             avg.fX += pt.fX;
     96             avg.fY += pt.fY;
     97         }
     98         SkScalar denom = SK_Scalar1 / count;
     99         avg.scale(denom);
    100         *c = avg;
    101     } else {
    102         area *= 3;
    103         area = SkScalarDiv(SK_Scalar1, area);
    104         center.fX = SkScalarMul(center.fX, area);
    105         center.fY = SkScalarMul(center.fY, area);
    106         // undo the translate of p0 to the origin.
    107         *c = center + p0;
    108     }
    109     SkASSERT(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
    110 }
    111 
    112 static void compute_vectors(SegmentArray* segments,
    113                             SkPoint* fanPt,
    114                             SkPath::Direction dir,
    115                             int* vCount,
    116                             int* iCount) {
    117     center_of_mass(*segments, fanPt);
    118     int count = segments->count();
    119 
    120     // Make the normals point towards the outside
    121     GrPoint::Side normSide;
    122     if (dir == SkPath::kCCW_Direction) {
    123         normSide = GrPoint::kRight_Side;
    124     } else {
    125         normSide = GrPoint::kLeft_Side;
    126     }
    127 
    128     *vCount = 0;
    129     *iCount = 0;
    130     // compute normals at all points
    131     for (int a = 0; a < count; ++a) {
    132         Segment& sega = (*segments)[a];
    133         int b = (a + 1) % count;
    134         Segment& segb = (*segments)[b];
    135 
    136         const GrPoint* prevPt = &sega.endPt();
    137         int n = segb.countPoints();
    138         for (int p = 0; p < n; ++p) {
    139             segb.fNorms[p] = segb.fPts[p] - *prevPt;
    140             segb.fNorms[p].normalize();
    141             segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
    142             prevPt = &segb.fPts[p];
    143         }
    144         if (Segment::kLine == segb.fType) {
    145             *vCount += 5;
    146             *iCount += 9;
    147         } else {
    148             *vCount += 6;
    149             *iCount += 12;
    150         }
    151     }
    152 
    153     // compute mid-vectors where segments meet. TODO: Detect shallow corners
    154     // and leave out the wedges and close gaps by stitching segments together.
    155     for (int a = 0; a < count; ++a) {
    156         const Segment& sega = (*segments)[a];
    157         int b = (a + 1) % count;
    158         Segment& segb = (*segments)[b];
    159         segb.fMid = segb.fNorms[0] + sega.endNorm();
    160         segb.fMid.normalize();
    161         // corner wedges
    162         *vCount += 4;
    163         *iCount += 6;
    164     }
    165 }
    166 
    167 struct DegenerateTestData {
    168     DegenerateTestData() { fStage = kInitial; }
    169     bool isDegenerate() const { return kNonDegenerate != fStage; }
    170     enum {
    171         kInitial,
    172         kPoint,
    173         kLine,
    174         kNonDegenerate
    175     }           fStage;
    176     GrPoint     fFirstPoint;
    177     GrVec       fLineNormal;
    178     SkScalar    fLineC;
    179 };
    180 
    181 static const SkScalar kClose = (SK_Scalar1 / 16);
    182 static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose);
    183 
    184 static void update_degenerate_test(DegenerateTestData* data, const GrPoint& pt) {
    185     switch (data->fStage) {
    186         case DegenerateTestData::kInitial:
    187             data->fFirstPoint = pt;
    188             data->fStage = DegenerateTestData::kPoint;
    189             break;
    190         case DegenerateTestData::kPoint:
    191             if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) {
    192                 data->fLineNormal = pt - data->fFirstPoint;
    193                 data->fLineNormal.normalize();
    194                 data->fLineNormal.setOrthog(data->fLineNormal);
    195                 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
    196                 data->fStage = DegenerateTestData::kLine;
    197             }
    198             break;
    199         case DegenerateTestData::kLine:
    200             if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
    201                 data->fStage = DegenerateTestData::kNonDegenerate;
    202             }
    203         case DegenerateTestData::kNonDegenerate:
    204             break;
    205         default:
    206             GrCrash("Unexpected degenerate test stage.");
    207     }
    208 }
    209 
    210 static inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) {
    211     if (!path.cheapComputeDirection(dir)) {
    212         return false;
    213     }
    214     // check whether m reverses the orientation
    215     SkASSERT(!m.hasPerspective());
    216     SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
    217                       SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
    218     if (det2x2 < 0) {
    219         *dir = SkPath::OppositeDirection(*dir);
    220     }
    221     return true;
    222 }
    223 
    224 static inline void add_line_to_segment(const SkPoint& pt,
    225                                        SegmentArray* segments,
    226                                        SkRect* devBounds) {
    227     segments->push_back();
    228     segments->back().fType = Segment::kLine;
    229     segments->back().fPts[0] = pt;
    230     devBounds->growToInclude(pt.fX, pt.fY);
    231 }
    232 
    233 #ifdef SK_DEBUG
    234 static inline bool contains_inclusive(const SkRect& rect, const SkPoint& p) {
    235     return p.fX >= rect.fLeft && p.fX <= rect.fRight && p.fY >= rect.fTop && p.fY <= rect.fBottom;
    236 }
    237 #endif
    238 
    239 static inline void add_quad_segment(const SkPoint pts[3],
    240                                     SegmentArray* segments,
    241                                     SkRect* devBounds) {
    242     if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
    243         if (pts[0] != pts[2]) {
    244             add_line_to_segment(pts[2], segments, devBounds);
    245         }
    246     } else {
    247         segments->push_back();
    248         segments->back().fType = Segment::kQuad;
    249         segments->back().fPts[0] = pts[1];
    250         segments->back().fPts[1] = pts[2];
    251         SkASSERT(contains_inclusive(*devBounds, pts[0]));
    252         devBounds->growToInclude(pts + 1, 2);
    253     }
    254 }
    255 
    256 static inline void add_cubic_segments(const SkPoint pts[4],
    257                                       SkPath::Direction dir,
    258                                       SegmentArray* segments,
    259                                       SkRect* devBounds) {
    260     SkSTArray<15, SkPoint, true> quads;
    261     GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
    262     int count = quads.count();
    263     for (int q = 0; q < count; q += 3) {
    264         add_quad_segment(&quads[q], segments, devBounds);
    265     }
    266 }
    267 
    268 static bool get_segments(const SkPath& path,
    269                          const SkMatrix& m,
    270                          SegmentArray* segments,
    271                          SkPoint* fanPt,
    272                          int* vCount,
    273                          int* iCount,
    274                          SkRect* devBounds) {
    275     SkPath::Iter iter(path, true);
    276     // This renderer over-emphasizes very thin path regions. We use the distance
    277     // to the path from the sample to compute coverage. Every pixel intersected
    278     // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
    279     // notice that the sample may be close to a very thin area of the path and
    280     // thus should be very light. This is particularly egregious for degenerate
    281     // line paths. We detect paths that are very close to a line (zero area) and
    282     // draw nothing.
    283     DegenerateTestData degenerateData;
    284     SkPath::Direction dir;
    285     // get_direction can fail for some degenerate paths.
    286     if (!get_direction(path, m, &dir)) {
    287         return false;
    288     }
    289 
    290     for (;;) {
    291         GrPoint pts[4];
    292         SkPath::Verb verb = iter.next(pts);
    293         switch (verb) {
    294             case SkPath::kMove_Verb:
    295                 m.mapPoints(pts, 1);
    296                 update_degenerate_test(&degenerateData, pts[0]);
    297                 devBounds->set(pts->fX, pts->fY, pts->fX, pts->fY);
    298                 break;
    299             case SkPath::kLine_Verb: {
    300                 m.mapPoints(&pts[1], 1);
    301                 update_degenerate_test(&degenerateData, pts[1]);
    302                 add_line_to_segment(pts[1], segments, devBounds);
    303                 break;
    304             }
    305             case SkPath::kQuad_Verb:
    306                 m.mapPoints(pts, 3);
    307                 update_degenerate_test(&degenerateData, pts[1]);
    308                 update_degenerate_test(&degenerateData, pts[2]);
    309                 add_quad_segment(pts, segments, devBounds);
    310                 break;
    311             case SkPath::kCubic_Verb: {
    312                 m.mapPoints(pts, 4);
    313                 update_degenerate_test(&degenerateData, pts[1]);
    314                 update_degenerate_test(&degenerateData, pts[2]);
    315                 update_degenerate_test(&degenerateData, pts[3]);
    316                 add_cubic_segments(pts, dir, segments, devBounds);
    317                 break;
    318             };
    319             case SkPath::kDone_Verb:
    320                 if (degenerateData.isDegenerate()) {
    321                     return false;
    322                 } else {
    323                     compute_vectors(segments, fanPt, dir, vCount, iCount);
    324                     return true;
    325                 }
    326             default:
    327                 break;
    328         }
    329     }
    330 }
    331 
    332 struct QuadVertex {
    333     GrPoint  fPos;
    334     GrPoint  fUV;
    335     SkScalar fD0;
    336     SkScalar fD1;
    337 };
    338 
    339 struct Draw {
    340     Draw() : fVertexCnt(0), fIndexCnt(0) {}
    341     int fVertexCnt;
    342     int fIndexCnt;
    343 };
    344 
    345 typedef SkTArray<Draw, true> DrawArray;
    346 
    347 static void create_vertices(const SegmentArray&  segments,
    348                             const SkPoint& fanPt,
    349                             DrawArray*     draws,
    350                             QuadVertex*    verts,
    351                             uint16_t*      idxs) {
    352     Draw* draw = &draws->push_back();
    353     // alias just to make vert/index assignments easier to read.
    354     int* v = &draw->fVertexCnt;
    355     int* i = &draw->fIndexCnt;
    356 
    357     int count = segments.count();
    358     for (int a = 0; a < count; ++a) {
    359         const Segment& sega = segments[a];
    360         int b = (a + 1) % count;
    361         const Segment& segb = segments[b];
    362 
    363         // Check whether adding the verts for this segment to the current draw would cause index
    364         // values to overflow.
    365         int vCount = 4;
    366         if (Segment::kLine == segb.fType) {
    367             vCount += 5;
    368         } else {
    369             vCount += 6;
    370         }
    371         if (draw->fVertexCnt + vCount > (1 << 16)) {
    372             verts += *v;
    373             idxs += *i;
    374             draw = &draws->push_back();
    375             v = &draw->fVertexCnt;
    376             i = &draw->fIndexCnt;
    377         }
    378 
    379         // FIXME: These tris are inset in the 1 unit arc around the corner
    380         verts[*v + 0].fPos = sega.endPt();
    381         verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
    382         verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
    383         verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
    384         verts[*v + 0].fUV.set(0,0);
    385         verts[*v + 1].fUV.set(0,-SK_Scalar1);
    386         verts[*v + 2].fUV.set(0,-SK_Scalar1);
    387         verts[*v + 3].fUV.set(0,-SK_Scalar1);
    388         verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
    389         verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
    390         verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
    391         verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
    392 
    393         idxs[*i + 0] = *v + 0;
    394         idxs[*i + 1] = *v + 2;
    395         idxs[*i + 2] = *v + 1;
    396         idxs[*i + 3] = *v + 0;
    397         idxs[*i + 4] = *v + 3;
    398         idxs[*i + 5] = *v + 2;
    399 
    400         *v += 4;
    401         *i += 6;
    402 
    403         if (Segment::kLine == segb.fType) {
    404             verts[*v + 0].fPos = fanPt;
    405             verts[*v + 1].fPos = sega.endPt();
    406             verts[*v + 2].fPos = segb.fPts[0];
    407 
    408             verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
    409             verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
    410 
    411             // we draw the line edge as a degenerate quad (u is 0, v is the
    412             // signed distance to the edge)
    413             SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
    414                                                         verts[*v + 2].fPos);
    415             verts[*v + 0].fUV.set(0, dist);
    416             verts[*v + 1].fUV.set(0, 0);
    417             verts[*v + 2].fUV.set(0, 0);
    418             verts[*v + 3].fUV.set(0, -SK_Scalar1);
    419             verts[*v + 4].fUV.set(0, -SK_Scalar1);
    420 
    421             verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
    422             verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
    423             verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
    424             verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
    425             verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
    426 
    427             idxs[*i + 0] = *v + 0;
    428             idxs[*i + 1] = *v + 2;
    429             idxs[*i + 2] = *v + 1;
    430 
    431             idxs[*i + 3] = *v + 3;
    432             idxs[*i + 4] = *v + 1;
    433             idxs[*i + 5] = *v + 2;
    434 
    435             idxs[*i + 6] = *v + 4;
    436             idxs[*i + 7] = *v + 3;
    437             idxs[*i + 8] = *v + 2;
    438 
    439             *v += 5;
    440             *i += 9;
    441         } else {
    442             GrPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
    443 
    444             GrVec midVec = segb.fNorms[0] + segb.fNorms[1];
    445             midVec.normalize();
    446 
    447             verts[*v + 0].fPos = fanPt;
    448             verts[*v + 1].fPos = qpts[0];
    449             verts[*v + 2].fPos = qpts[2];
    450             verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
    451             verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
    452             verts[*v + 5].fPos = qpts[1] + midVec;
    453 
    454             SkScalar c = segb.fNorms[0].dot(qpts[0]);
    455             verts[*v + 0].fD0 =  -segb.fNorms[0].dot(fanPt) + c;
    456             verts[*v + 1].fD0 =  0.f;
    457             verts[*v + 2].fD0 =  -segb.fNorms[0].dot(qpts[2]) + c;
    458             verts[*v + 3].fD0 = -SK_ScalarMax/100;
    459             verts[*v + 4].fD0 = -SK_ScalarMax/100;
    460             verts[*v + 5].fD0 = -SK_ScalarMax/100;
    461 
    462             c = segb.fNorms[1].dot(qpts[2]);
    463             verts[*v + 0].fD1 =  -segb.fNorms[1].dot(fanPt) + c;
    464             verts[*v + 1].fD1 =  -segb.fNorms[1].dot(qpts[0]) + c;
    465             verts[*v + 2].fD1 =  0.f;
    466             verts[*v + 3].fD1 = -SK_ScalarMax/100;
    467             verts[*v + 4].fD1 = -SK_ScalarMax/100;
    468             verts[*v + 5].fD1 = -SK_ScalarMax/100;
    469 
    470             GrPathUtils::QuadUVMatrix toUV(qpts);
    471             toUV.apply<6, sizeof(QuadVertex), sizeof(GrPoint)>(verts + *v);
    472 
    473             idxs[*i + 0] = *v + 3;
    474             idxs[*i + 1] = *v + 1;
    475             idxs[*i + 2] = *v + 2;
    476             idxs[*i + 3] = *v + 4;
    477             idxs[*i + 4] = *v + 3;
    478             idxs[*i + 5] = *v + 2;
    479 
    480             idxs[*i + 6] = *v + 5;
    481             idxs[*i + 7] = *v + 3;
    482             idxs[*i + 8] = *v + 4;
    483 
    484             idxs[*i +  9] = *v + 0;
    485             idxs[*i + 10] = *v + 2;
    486             idxs[*i + 11] = *v + 1;
    487 
    488             *v += 6;
    489             *i += 12;
    490         }
    491     }
    492 }
    493 
    494 ///////////////////////////////////////////////////////////////////////////////
    495 
    496 /*
    497  * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
    498  * two components of the vertex attribute. Coverage is based on signed
    499  * distance with negative being inside, positive outside. The edge is specified in
    500  * window space (y-down). If either the third or fourth component of the interpolated
    501  * vertex coord is > 0 then the pixel is considered outside the edge. This is used to
    502  * attempt to trim to a portion of the infinite quad.
    503  * Requires shader derivative instruction support.
    504  */
    505 
    506 class QuadEdgeEffect : public GrVertexEffect {
    507 public:
    508 
    509     static GrEffectRef* Create() {
    510         GR_CREATE_STATIC_EFFECT(gQuadEdgeEffect, QuadEdgeEffect, ());
    511         gQuadEdgeEffect->ref();
    512         return gQuadEdgeEffect;
    513     }
    514 
    515     virtual ~QuadEdgeEffect() {}
    516 
    517     static const char* Name() { return "QuadEdge"; }
    518 
    519     virtual void getConstantColorComponents(GrColor* color,
    520                                             uint32_t* validFlags) const SK_OVERRIDE {
    521         *validFlags = 0;
    522     }
    523 
    524     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
    525         return GrTBackendEffectFactory<QuadEdgeEffect>::getInstance();
    526     }
    527 
    528     class GLEffect : public GrGLVertexEffect {
    529     public:
    530         GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
    531             : INHERITED (factory) {}
    532 
    533         virtual void emitCode(GrGLFullShaderBuilder* builder,
    534                               const GrDrawEffect& drawEffect,
    535                               EffectKey key,
    536                               const char* outputColor,
    537                               const char* inputColor,
    538                               const TransformedCoordsArray&,
    539                               const TextureSamplerArray& samplers) SK_OVERRIDE {
    540             const char *vsName, *fsName;
    541             const SkString* attrName =
    542                 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
    543             builder->fsCodeAppendf("\t\tfloat edgeAlpha;\n");
    544 
    545             SkAssertResult(builder->enableFeature(
    546                                               GrGLShaderBuilder::kStandardDerivatives_GLSLFeature));
    547             builder->addVarying(kVec4f_GrSLType, "QuadEdge", &vsName, &fsName);
    548 
    549             // keep the derivative instructions outside the conditional
    550             builder->fsCodeAppendf("\t\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
    551             builder->fsCodeAppendf("\t\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
    552             builder->fsCodeAppendf("\t\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName);
    553             // today we know z and w are in device space. We could use derivatives
    554             builder->fsCodeAppendf("\t\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName,
    555                                     fsName);
    556             builder->fsCodeAppendf ("\t\t} else {\n");
    557             builder->fsCodeAppendf("\t\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
    558                                    "\t\t\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
    559                                    fsName, fsName);
    560             builder->fsCodeAppendf("\t\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName,
    561                                     fsName);
    562             builder->fsCodeAppendf("\t\t\tedgeAlpha = "
    563                                    "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n\t\t}\n");
    564 
    565 
    566             builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
    567                                    (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
    568 
    569             builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
    570         }
    571 
    572         static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
    573             return 0x0;
    574         }
    575 
    576         virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
    577 
    578     private:
    579         typedef GrGLVertexEffect INHERITED;
    580     };
    581 
    582 private:
    583     QuadEdgeEffect() {
    584         this->addVertexAttrib(kVec4f_GrSLType);
    585     }
    586 
    587     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
    588         return true;
    589     }
    590 
    591     GR_DECLARE_EFFECT_TEST;
    592 
    593     typedef GrVertexEffect INHERITED;
    594 };
    595 
    596 GR_DEFINE_EFFECT_TEST(QuadEdgeEffect);
    597 
    598 GrEffectRef* QuadEdgeEffect::TestCreate(SkRandom* random,
    599                                         GrContext*,
    600                                         const GrDrawTargetCaps& caps,
    601                                         GrTexture*[]) {
    602     // Doesn't work without derivative instructions.
    603     return caps.shaderDerivativeSupport() ? QuadEdgeEffect::Create() : NULL;
    604 }
    605 
    606 ///////////////////////////////////////////////////////////////////////////////
    607 
    608 bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
    609                                          const SkStrokeRec& stroke,
    610                                          const GrDrawTarget* target,
    611                                          bool antiAlias) const {
    612     return (target->caps()->shaderDerivativeSupport() && antiAlias &&
    613             stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
    614 }
    615 
    616 namespace {
    617 
    618 // position + edge
    619 extern const GrVertexAttrib gPathAttribs[] = {
    620     {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
    621     {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
    622 };
    623 
    624 };
    625 
    626 bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath,
    627                                         const SkStrokeRec&,
    628                                         GrDrawTarget* target,
    629                                         bool antiAlias) {
    630 
    631     const SkPath* path = &origPath;
    632     if (path->isEmpty()) {
    633         return true;
    634     }
    635 
    636     SkMatrix viewMatrix = target->getDrawState().getViewMatrix();
    637     GrDrawTarget::AutoStateRestore asr;
    638     if (!asr.setIdentity(target, GrDrawTarget::kPreserve_ASRInit)) {
    639         return false;
    640     }
    641     GrDrawState* drawState = target->drawState();
    642 
    643     // We use the fact that SkPath::transform path does subdivision based on
    644     // perspective. Otherwise, we apply the view matrix when copying to the
    645     // segment representation.
    646     SkPath tmpPath;
    647     if (viewMatrix.hasPerspective()) {
    648         origPath.transform(viewMatrix, &tmpPath);
    649         path = &tmpPath;
    650         viewMatrix = SkMatrix::I();
    651     }
    652 
    653     QuadVertex *verts;
    654     uint16_t* idxs;
    655 
    656     int vCount;
    657     int iCount;
    658     enum {
    659         kPreallocSegmentCnt = 512 / sizeof(Segment),
    660         kPreallocDrawCnt = 4,
    661     };
    662     SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
    663     SkPoint fanPt;
    664 
    665     // We can't simply use the path bounds because we may degenerate cubics to quads which produces
    666     // new control points outside the original convex hull.
    667     SkRect devBounds;
    668     if (!get_segments(*path, viewMatrix, &segments, &fanPt, &vCount, &iCount, &devBounds)) {
    669         return false;
    670     }
    671 
    672     // Our computed verts should all be within one pixel of the segment control points.
    673     devBounds.outset(SK_Scalar1, SK_Scalar1);
    674 
    675     drawState->setVertexAttribs<gPathAttribs>(SK_ARRAY_COUNT(gPathAttribs));
    676 
    677     static const int kEdgeAttrIndex = 1;
    678     GrEffectRef* quadEffect = QuadEdgeEffect::Create();
    679     drawState->addCoverageEffect(quadEffect, kEdgeAttrIndex)->unref();
    680 
    681     GrDrawTarget::AutoReleaseGeometry arg(target, vCount, iCount);
    682     if (!arg.succeeded()) {
    683         return false;
    684     }
    685     SkASSERT(sizeof(QuadVertex) == drawState->getVertexSize());
    686     verts = reinterpret_cast<QuadVertex*>(arg.vertices());
    687     idxs = reinterpret_cast<uint16_t*>(arg.indices());
    688 
    689     SkSTArray<kPreallocDrawCnt, Draw, true> draws;
    690     create_vertices(segments, fanPt, &draws, verts, idxs);
    691 
    692     // Check devBounds
    693 #ifdef SK_DEBUG
    694     SkRect tolDevBounds = devBounds;
    695     tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000);
    696     SkRect actualBounds;
    697     actualBounds.set(verts[0].fPos, verts[1].fPos);
    698     for (int i = 2; i < vCount; ++i) {
    699         actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY);
    700     }
    701     SkASSERT(tolDevBounds.contains(actualBounds));
    702 #endif
    703 
    704     int vOffset = 0;
    705     for (int i = 0; i < draws.count(); ++i) {
    706         const Draw& draw = draws[i];
    707         target->drawIndexed(kTriangles_GrPrimitiveType,
    708                             vOffset,  // start vertex
    709                             0,        // start index
    710                             draw.fVertexCnt,
    711                             draw.fIndexCnt,
    712                             &devBounds);
    713         vOffset += draw.fVertexCnt;
    714     }
    715 
    716     return true;
    717 }
    718