Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2014 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 "GrDashingEffect.h"
      9 
     10 #include "GrBatch.h"
     11 #include "GrBatchTarget.h"
     12 #include "GrBatchTest.h"
     13 #include "GrGeometryProcessor.h"
     14 #include "GrContext.h"
     15 #include "GrCoordTransform.h"
     16 #include "GrDefaultGeoProcFactory.h"
     17 #include "GrDrawTarget.h"
     18 #include "GrDrawTargetCaps.h"
     19 #include "GrInvariantOutput.h"
     20 #include "GrProcessor.h"
     21 #include "GrStrokeInfo.h"
     22 #include "GrVertexBuffer.h"
     23 #include "SkGr.h"
     24 #include "gl/GrGLGeometryProcessor.h"
     25 #include "gl/GrGLProcessor.h"
     26 #include "gl/GrGLSL.h"
     27 #include "gl/builders/GrGLProgramBuilder.h"
     28 
     29 ///////////////////////////////////////////////////////////////////////////////
     30 
     31 // Returns whether or not the gpu can fast path the dash line effect.
     32 bool GrDashingEffect::CanDrawDashLine(const SkPoint pts[2], const GrStrokeInfo& strokeInfo,
     33                                       const SkMatrix& viewMatrix) {
     34     // Pts must be either horizontal or vertical in src space
     35     if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) {
     36         return false;
     37     }
     38 
     39     // May be able to relax this to include skew. As of now cannot do perspective
     40     // because of the non uniform scaling of bloating a rect
     41     if (!viewMatrix.preservesRightAngles()) {
     42         return false;
     43     }
     44 
     45     if (!strokeInfo.isDashed() || 2 != strokeInfo.getDashCount()) {
     46         return false;
     47     }
     48 
     49     const SkScalar* intervals = strokeInfo.getDashIntervals();
     50     if (0 == intervals[0] && 0 == intervals[1]) {
     51         return false;
     52     }
     53 
     54     SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
     55     // Current we do don't handle Round or Square cap dashes
     56     if (SkPaint::kRound_Cap == cap && intervals[0] != 0.f) {
     57         return false;
     58     }
     59 
     60     return true;
     61 }
     62 
     63 namespace {
     64 struct DashLineVertex {
     65     SkPoint fPos;
     66     SkPoint fDashPos;
     67     SkScalar fIntervalLength;
     68     SkRect fRect;
     69 };
     70 struct DashCircleVertex {
     71     SkPoint fPos;
     72     SkPoint fDashPos;
     73     SkScalar fIntervalLength;
     74     SkScalar fRadius;
     75     SkScalar fCenterX;
     76 };
     77 
     78 enum DashAAMode {
     79     kBW_DashAAMode,
     80     kEdgeAA_DashAAMode,
     81     kMSAA_DashAAMode,
     82 
     83     kDashAAModeCount,
     84 };
     85 };
     86 
     87 static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale,
     88                             const SkMatrix& viewMatrix, const SkPoint pts[2]) {
     89     SkVector vecSrc = pts[1] - pts[0];
     90     SkScalar magSrc = vecSrc.length();
     91     SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0;
     92     vecSrc.scale(invSrc);
     93 
     94     SkVector vecSrcPerp;
     95     vecSrc.rotateCW(&vecSrcPerp);
     96     viewMatrix.mapVectors(&vecSrc, 1);
     97     viewMatrix.mapVectors(&vecSrcPerp, 1);
     98 
     99     // parallelScale tells how much to scale along the line parallel to the dash line
    100     // perpScale tells how much to scale in the direction perpendicular to the dash line
    101     *parallelScale = vecSrc.length();
    102     *perpScale = vecSrcPerp.length();
    103 }
    104 
    105 // calculates the rotation needed to aligned pts to the x axis with pts[0] < pts[1]
    106 // Stores the rotation matrix in rotMatrix, and the mapped points in ptsRot
    107 static void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint ptsRot[2] = NULL) {
    108     SkVector vec = pts[1] - pts[0];
    109     SkScalar mag = vec.length();
    110     SkScalar inv = mag ? SkScalarInvert(mag) : 0;
    111 
    112     vec.scale(inv);
    113     rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
    114     if (ptsRot) {
    115         rotMatrix->mapPoints(ptsRot, pts, 2);
    116         // correction for numerical issues if map doesn't make ptsRot exactly horizontal
    117         ptsRot[1].fY = pts[0].fY;
    118     }
    119 }
    120 
    121 // Assumes phase < sum of all intervals
    122 static SkScalar calc_start_adjustment(const SkScalar intervals[2], SkScalar phase) {
    123     SkASSERT(phase < intervals[0] + intervals[1]);
    124     if (phase >= intervals[0] && phase != 0) {
    125         SkScalar srcIntervalLen = intervals[0] + intervals[1];
    126         return srcIntervalLen - phase;
    127     }
    128     return 0;
    129 }
    130 
    131 static SkScalar calc_end_adjustment(const SkScalar intervals[2], const SkPoint pts[2],
    132                                     SkScalar phase, SkScalar* endingInt) {
    133     if (pts[1].fX <= pts[0].fX) {
    134         return 0;
    135     }
    136     SkScalar srcIntervalLen = intervals[0] + intervals[1];
    137     SkScalar totalLen = pts[1].fX - pts[0].fX;
    138     SkScalar temp = totalLen / srcIntervalLen;
    139     SkScalar numFullIntervals = SkScalarFloorToScalar(temp);
    140     *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase;
    141     temp = *endingInt / srcIntervalLen;
    142     *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen;
    143     if (0 == *endingInt) {
    144         *endingInt = srcIntervalLen;
    145     }
    146     if (*endingInt > intervals[0]) {
    147         if (0 == intervals[0]) {
    148             *endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps)
    149         }
    150         return *endingInt - intervals[0];
    151     }
    152     return 0;
    153 }
    154 
    155 enum DashCap {
    156     kRound_DashCap,
    157     kNonRound_DashCap,
    158 };
    159 
    160 static int kDashVertices = 4;
    161 
    162 template <typename T>
    163 void setup_dashed_rect_common(const SkRect& rect, const SkMatrix& matrix, T* vertices, int idx,
    164                               SkScalar offset, SkScalar bloatX, SkScalar bloatY, SkScalar len,
    165                               SkScalar stroke) {
    166     SkScalar startDashX = offset - bloatX;
    167     SkScalar endDashX = offset + len + bloatX;
    168     SkScalar startDashY = -stroke - bloatY;
    169     SkScalar endDashY = stroke + bloatY;
    170     vertices[idx].fDashPos = SkPoint::Make(startDashX , startDashY);
    171     vertices[idx + 1].fDashPos = SkPoint::Make(startDashX, endDashY);
    172     vertices[idx + 2].fDashPos = SkPoint::Make(endDashX, endDashY);
    173     vertices[idx + 3].fDashPos = SkPoint::Make(endDashX, startDashY);
    174 
    175     vertices[idx].fPos = SkPoint::Make(rect.fLeft, rect.fTop);
    176     vertices[idx + 1].fPos = SkPoint::Make(rect.fLeft, rect.fBottom);
    177     vertices[idx + 2].fPos = SkPoint::Make(rect.fRight, rect.fBottom);
    178     vertices[idx + 3].fPos = SkPoint::Make(rect.fRight, rect.fTop);
    179 
    180     matrix.mapPointsWithStride(&vertices[idx].fPos, sizeof(T), 4);
    181 }
    182 
    183 static void setup_dashed_rect(const SkRect& rect, void* vertices, int idx,
    184                               const SkMatrix& matrix, SkScalar offset, SkScalar bloatX,
    185                               SkScalar bloatY, SkScalar len, SkScalar stroke,
    186                               SkScalar startInterval, SkScalar endInterval, SkScalar strokeWidth,
    187                               DashCap cap, const size_t vertexStride) {
    188     SkScalar intervalLength = startInterval + endInterval;
    189 
    190     if (kRound_DashCap == cap) {
    191         SkASSERT(vertexStride == sizeof(DashCircleVertex));
    192         DashCircleVertex* verts = reinterpret_cast<DashCircleVertex*>(vertices);
    193 
    194         setup_dashed_rect_common<DashCircleVertex>(rect, matrix, verts, idx, offset, bloatX,
    195                                                    bloatY, len, stroke);
    196 
    197         SkScalar radius = SkScalarHalf(strokeWidth) - 0.5f;
    198         SkScalar centerX = SkScalarHalf(endInterval);
    199 
    200         for (int i = 0; i < kDashVertices; i++) {
    201             verts[idx + i].fIntervalLength = intervalLength;
    202             verts[idx + i].fRadius = radius;
    203             verts[idx + i].fCenterX = centerX;
    204         }
    205 
    206     } else {
    207         SkASSERT(kNonRound_DashCap == cap && vertexStride == sizeof(DashLineVertex));
    208         DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(vertices);
    209 
    210         setup_dashed_rect_common<DashLineVertex>(rect, matrix, verts, idx, offset, bloatX,
    211                                                  bloatY, len, stroke);
    212 
    213         SkScalar halfOffLen = SkScalarHalf(endInterval);
    214         SkScalar halfStroke = SkScalarHalf(strokeWidth);
    215         SkRect rectParam;
    216         rectParam.set(halfOffLen + 0.5f, -halfStroke + 0.5f,
    217                       halfOffLen + startInterval - 0.5f, halfStroke - 0.5f);
    218         for (int i = 0; i < kDashVertices; i++) {
    219             verts[idx + i].fIntervalLength = intervalLength;
    220             verts[idx + i].fRect = rectParam;
    221         }
    222     }
    223 }
    224 
    225 static void setup_dashed_rect_pos(const SkRect& rect, int idx, const SkMatrix& matrix,
    226                                   SkPoint* verts) {
    227     verts[idx] = SkPoint::Make(rect.fLeft, rect.fTop);
    228     verts[idx + 1] = SkPoint::Make(rect.fLeft, rect.fBottom);
    229     verts[idx + 2] = SkPoint::Make(rect.fRight, rect.fBottom);
    230     verts[idx + 3] = SkPoint::Make(rect.fRight, rect.fTop);
    231     matrix.mapPoints(&verts[idx], 4);
    232 }
    233 
    234 
    235 /**
    236  * An GrGeometryProcessor that renders a dashed line.
    237  * This GrGeometryProcessor is meant for dashed lines that only have a single on/off interval pair.
    238  * Bounding geometry is rendered and the effect computes coverage based on the fragment's
    239  * position relative to the dashed line.
    240  */
    241 static GrGeometryProcessor* create_dash_gp(GrColor,
    242                                            DashAAMode aaMode,
    243                                            DashCap cap,
    244                                            const SkMatrix& localMatrix);
    245 
    246 class DashBatch : public GrBatch {
    247 public:
    248     struct Geometry {
    249         GrColor fColor;
    250         SkMatrix fViewMatrix;
    251         SkMatrix fSrcRotInv;
    252         SkPoint fPtsRot[2];
    253         SkScalar fSrcStrokeWidth;
    254         SkScalar fPhase;
    255         SkScalar fIntervals[2];
    256         SkScalar fParallelScale;
    257         SkScalar fPerpendicularScale;
    258     };
    259 
    260     static GrBatch* Create(const Geometry& geometry, SkPaint::Cap cap, DashAAMode aaMode,
    261                            bool fullDash) {
    262         return SkNEW_ARGS(DashBatch, (geometry, cap, aaMode, fullDash));
    263     }
    264 
    265     const char* name() const override { return "DashBatch"; }
    266 
    267     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
    268         // When this is called on a batch, there is only one geometry bundle
    269         out->setKnownFourComponents(fGeoData[0].fColor);
    270     }
    271     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
    272         out->setUnknownSingleComponent();
    273     }
    274 
    275     void initBatchTracker(const GrPipelineInfo& init) override {
    276         // Handle any color overrides
    277         if (init.fColorIgnored) {
    278             fGeoData[0].fColor = GrColor_ILLEGAL;
    279         } else if (GrColor_ILLEGAL != init.fOverrideColor) {
    280             fGeoData[0].fColor = init.fOverrideColor;
    281         }
    282 
    283         // setup batch properties
    284         fBatch.fColorIgnored = init.fColorIgnored;
    285         fBatch.fColor = fGeoData[0].fColor;
    286         fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
    287         fBatch.fCoverageIgnored = init.fCoverageIgnored;
    288     }
    289 
    290     struct DashDraw {
    291         SkScalar fStartOffset;
    292         SkScalar fStrokeWidth;
    293         SkScalar fLineLength;
    294         SkScalar fHalfDevStroke;
    295         SkScalar fDevBloatX;
    296         SkScalar fDevBloatY;
    297         bool fLineDone;
    298         bool fHasStartRect;
    299         bool fHasEndRect;
    300     };
    301 
    302     void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
    303         int instanceCount = fGeoData.count();
    304 
    305         SkMatrix invert;
    306         if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
    307             SkDebugf("Failed to invert\n");
    308             return;
    309         }
    310 
    311         SkPaint::Cap cap = this->cap();
    312 
    313         SkAutoTUnref<const GrGeometryProcessor> gp;
    314 
    315         bool isRoundCap = SkPaint::kRound_Cap == cap;
    316         DashCap capType = isRoundCap ? kRound_DashCap : kNonRound_DashCap;
    317         if (this->fullDash()) {
    318             gp.reset(create_dash_gp(this->color(), this->aaMode(), capType, invert));
    319         } else {
    320             // Set up the vertex data for the line and start/end dashes
    321             gp.reset(GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
    322                                                      this->color(),
    323                                                      SkMatrix::I(),
    324                                                      invert));
    325         }
    326 
    327         batchTarget->initDraw(gp, pipeline);
    328 
    329         // TODO remove this when batch is everywhere
    330         GrPipelineInfo init;
    331         init.fColorIgnored = fBatch.fColorIgnored;
    332         init.fOverrideColor = GrColor_ILLEGAL;
    333         init.fCoverageIgnored = fBatch.fCoverageIgnored;
    334         init.fUsesLocalCoords = this->usesLocalCoords();
    335         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
    336 
    337         // useAA here means Edge AA or MSAA
    338         bool useAA = this->aaMode() != kBW_DashAAMode;
    339         bool fullDash = this->fullDash();
    340 
    341         // We do two passes over all of the dashes.  First we setup the start, end, and bounds,
    342         // rectangles.  We preserve all of this work in the rects / draws arrays below.  Then we
    343         // iterate again over these decomposed dashes to generate vertices
    344         SkSTArray<128, SkRect, true> rects;
    345         SkSTArray<128, DashDraw, true> draws;
    346 
    347         int totalRectCount = 0;
    348         int rectOffset = 0;
    349         rects.push_back_n(3 * instanceCount);
    350         for (int i = 0; i < instanceCount; i++) {
    351             Geometry& args = fGeoData[i];
    352 
    353             bool hasCap = SkPaint::kButt_Cap != cap && 0 != args.fSrcStrokeWidth;
    354 
    355             // We always want to at least stroke out half a pixel on each side in device space
    356             // so 0.5f / perpScale gives us this min in src space
    357             SkScalar halfSrcStroke =
    358                     SkMaxScalar(args.fSrcStrokeWidth * 0.5f, 0.5f / args.fPerpendicularScale);
    359 
    360             SkScalar strokeAdj;
    361             if (!hasCap) {
    362                 strokeAdj = 0.f;
    363             } else {
    364                 strokeAdj = halfSrcStroke;
    365             }
    366 
    367             SkScalar startAdj = 0;
    368 
    369             bool lineDone = false;
    370 
    371             // Too simplify the algorithm, we always push back rects for start and end rect.
    372             // Otherwise we'd have to track start / end rects for each individual geometry
    373             SkRect& bounds = rects[rectOffset++];
    374             SkRect& startRect = rects[rectOffset++];
    375             SkRect& endRect = rects[rectOffset++];
    376 
    377             bool hasStartRect = false;
    378             // If we are using AA, check to see if we are drawing a partial dash at the start. If so
    379             // draw it separately here and adjust our start point accordingly
    380             if (useAA) {
    381                 if (args.fPhase > 0 && args.fPhase < args.fIntervals[0]) {
    382                     SkPoint startPts[2];
    383                     startPts[0] = args.fPtsRot[0];
    384                     startPts[1].fY = startPts[0].fY;
    385                     startPts[1].fX = SkMinScalar(startPts[0].fX + args.fIntervals[0] - args.fPhase,
    386                                                  args.fPtsRot[1].fX);
    387                     startRect.set(startPts, 2);
    388                     startRect.outset(strokeAdj, halfSrcStroke);
    389 
    390                     hasStartRect = true;
    391                     startAdj = args.fIntervals[0] + args.fIntervals[1] - args.fPhase;
    392                 }
    393             }
    394 
    395             // adjustments for start and end of bounding rect so we only draw dash intervals
    396             // contained in the original line segment.
    397             startAdj += calc_start_adjustment(args.fIntervals, args.fPhase);
    398             if (startAdj != 0) {
    399                 args.fPtsRot[0].fX += startAdj;
    400                 args.fPhase = 0;
    401             }
    402             SkScalar endingInterval = 0;
    403             SkScalar endAdj = calc_end_adjustment(args.fIntervals, args.fPtsRot, args.fPhase,
    404                                                   &endingInterval);
    405             args.fPtsRot[1].fX -= endAdj;
    406             if (args.fPtsRot[0].fX >= args.fPtsRot[1].fX) {
    407                 lineDone = true;
    408             }
    409 
    410             bool hasEndRect = false;
    411             // If we are using AA, check to see if we are drawing a partial dash at then end. If so
    412             // draw it separately here and adjust our end point accordingly
    413             if (useAA && !lineDone) {
    414                 // If we adjusted the end then we will not be drawing a partial dash at the end.
    415                 // If we didn't adjust the end point then we just need to make sure the ending
    416                 // dash isn't a full dash
    417                 if (0 == endAdj && endingInterval != args.fIntervals[0]) {
    418                     SkPoint endPts[2];
    419                     endPts[1] = args.fPtsRot[1];
    420                     endPts[0].fY = endPts[1].fY;
    421                     endPts[0].fX = endPts[1].fX - endingInterval;
    422 
    423                     endRect.set(endPts, 2);
    424                     endRect.outset(strokeAdj, halfSrcStroke);
    425 
    426                     hasEndRect = true;
    427                     endAdj = endingInterval + args.fIntervals[1];
    428 
    429                     args.fPtsRot[1].fX -= endAdj;
    430                     if (args.fPtsRot[0].fX >= args.fPtsRot[1].fX) {
    431                         lineDone = true;
    432                     }
    433                 }
    434             }
    435 
    436             if (startAdj != 0) {
    437                 args.fPhase = 0;
    438             }
    439 
    440             // Change the dashing info from src space into device space
    441             SkScalar* devIntervals = args.fIntervals;
    442             devIntervals[0] = args.fIntervals[0] * args.fParallelScale;
    443             devIntervals[1] = args.fIntervals[1] * args.fParallelScale;
    444             SkScalar devPhase = args.fPhase * args.fParallelScale;
    445             SkScalar strokeWidth = args.fSrcStrokeWidth * args.fPerpendicularScale;
    446 
    447             if ((strokeWidth < 1.f && useAA) || 0.f == strokeWidth) {
    448                 strokeWidth = 1.f;
    449             }
    450 
    451             SkScalar halfDevStroke = strokeWidth * 0.5f;
    452 
    453             if (SkPaint::kSquare_Cap == cap && 0 != args.fSrcStrokeWidth) {
    454                 // add cap to on interval and remove from off interval
    455                 devIntervals[0] += strokeWidth;
    456                 devIntervals[1] -= strokeWidth;
    457             }
    458             SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
    459 
    460             // For EdgeAA, we bloat in X & Y for both square and round caps.
    461             // For MSAA, we don't bloat at all for square caps, and bloat in Y only for round caps.
    462             SkScalar devBloatX = this->aaMode() == kEdgeAA_DashAAMode ? 0.5f : 0.0f;
    463             SkScalar devBloatY = (SkPaint::kRound_Cap == cap && this->aaMode() == kMSAA_DashAAMode)
    464                                  ? 0.5f : devBloatX;
    465 
    466             SkScalar bloatX = devBloatX / args.fParallelScale;
    467             SkScalar bloatY = devBloatY / args.fPerpendicularScale;
    468 
    469             if (devIntervals[1] <= 0.f && useAA) {
    470                 // Case when we end up drawing a solid AA rect
    471                 // Reset the start rect to draw this single solid rect
    472                 // but it requires to upload a new intervals uniform so we can mimic
    473                 // one giant dash
    474                 args.fPtsRot[0].fX -= hasStartRect ? startAdj : 0;
    475                 args.fPtsRot[1].fX += hasEndRect ? endAdj : 0;
    476                 startRect.set(args.fPtsRot, 2);
    477                 startRect.outset(strokeAdj, halfSrcStroke);
    478                 hasStartRect = true;
    479                 hasEndRect = false;
    480                 lineDone = true;
    481 
    482                 SkPoint devicePts[2];
    483                 args.fViewMatrix.mapPoints(devicePts, args.fPtsRot, 2);
    484                 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
    485                 if (hasCap) {
    486                     lineLength += 2.f * halfDevStroke;
    487                 }
    488                 devIntervals[0] = lineLength;
    489             }
    490 
    491             totalRectCount += !lineDone ? 1 : 0;
    492             totalRectCount += hasStartRect ? 1 : 0;
    493             totalRectCount += hasEndRect ? 1 : 0;
    494 
    495             if (SkPaint::kRound_Cap == cap && 0 != args.fSrcStrokeWidth) {
    496                 // need to adjust this for round caps to correctly set the dashPos attrib on
    497                 // vertices
    498                 startOffset -= halfDevStroke;
    499             }
    500 
    501             DashDraw& draw = draws.push_back();
    502             if (!lineDone) {
    503                 SkPoint devicePts[2];
    504                 args.fViewMatrix.mapPoints(devicePts, args.fPtsRot, 2);
    505                 draw.fLineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
    506                 if (hasCap) {
    507                     draw.fLineLength += 2.f * halfDevStroke;
    508                 }
    509 
    510                 bounds.set(args.fPtsRot[0].fX, args.fPtsRot[0].fY,
    511                            args.fPtsRot[1].fX, args.fPtsRot[1].fY);
    512                 bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke);
    513             }
    514 
    515             if (hasStartRect) {
    516                 SkASSERT(useAA);  // so that we know bloatX and bloatY have been set
    517                 startRect.outset(bloatX, bloatY);
    518             }
    519 
    520             if (hasEndRect) {
    521                 SkASSERT(useAA);  // so that we know bloatX and bloatY have been set
    522                 endRect.outset(bloatX, bloatY);
    523             }
    524 
    525             draw.fStartOffset = startOffset;
    526             draw.fDevBloatX = devBloatX;
    527             draw.fDevBloatY = devBloatY;
    528             draw.fHalfDevStroke = halfDevStroke;
    529             draw.fStrokeWidth = strokeWidth;
    530             draw.fHasStartRect = hasStartRect;
    531             draw.fLineDone = lineDone;
    532             draw.fHasEndRect = hasEndRect;
    533         }
    534 
    535         if (!totalRectCount) {
    536             return;
    537         }
    538 
    539         QuadHelper helper;
    540         void* vertices = helper.init(batchTarget, gp->getVertexStride(), totalRectCount);
    541         if (!vertices) {
    542             return;
    543         }
    544 
    545         int curVIdx = 0;
    546         int rectIndex = 0;
    547         for (int i = 0; i < instanceCount; i++) {
    548             Geometry& geom = fGeoData[i];
    549 
    550             if (!draws[i].fLineDone) {
    551                 if (fullDash) {
    552                     setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
    553                                       draws[i].fStartOffset, draws[i].fDevBloatX,
    554                                       draws[i].fDevBloatY, draws[i].fLineLength,
    555                                       draws[i].fHalfDevStroke, geom.fIntervals[0],
    556                                       geom.fIntervals[1], draws[i].fStrokeWidth,
    557                                       capType, gp->getVertexStride());
    558                 } else {
    559                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
    560                     SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
    561                     setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
    562                 }
    563                 curVIdx += 4;
    564             }
    565             rectIndex++;
    566 
    567             if (draws[i].fHasStartRect) {
    568                 if (fullDash) {
    569                     setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
    570                                       draws[i].fStartOffset, draws[i].fDevBloatX,
    571                                       draws[i].fDevBloatY, geom.fIntervals[0],
    572                                       draws[i].fHalfDevStroke, geom.fIntervals[0],
    573                                       geom.fIntervals[1], draws[i].fStrokeWidth, capType,
    574                                       gp->getVertexStride());
    575                 } else {
    576                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
    577                     SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
    578                     setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
    579                 }
    580                 curVIdx += 4;
    581             }
    582             rectIndex++;
    583 
    584             if (draws[i].fHasEndRect) {
    585                 if (fullDash) {
    586                     setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
    587                                       draws[i].fStartOffset, draws[i].fDevBloatX,
    588                                       draws[i].fDevBloatY, geom.fIntervals[0],
    589                                       draws[i].fHalfDevStroke, geom.fIntervals[0],
    590                                       geom.fIntervals[1], draws[i].fStrokeWidth, capType,
    591                                       gp->getVertexStride());
    592                 } else {
    593                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
    594                     SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
    595                     setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
    596                 }
    597                 curVIdx += 4;
    598             }
    599             rectIndex++;
    600         }
    601         SkASSERT(0 == (curVIdx % 4) && (curVIdx / 4) == totalRectCount);
    602         helper.issueDraw(batchTarget);
    603     }
    604 
    605     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
    606 
    607 private:
    608     DashBatch(const Geometry& geometry, SkPaint::Cap cap, DashAAMode aaMode, bool fullDash) {
    609         this->initClassID<DashBatch>();
    610         fGeoData.push_back(geometry);
    611 
    612         fBatch.fAAMode = aaMode;
    613         fBatch.fCap = cap;
    614         fBatch.fFullDash = fullDash;
    615 
    616         // compute bounds
    617         SkScalar halfStrokeWidth = 0.5f * geometry.fSrcStrokeWidth;
    618         SkScalar xBloat = SkPaint::kButt_Cap == cap ? 0 : halfStrokeWidth;
    619         fBounds.set(geometry.fPtsRot[0], geometry.fPtsRot[1]);
    620         fBounds.outset(xBloat, halfStrokeWidth);
    621 
    622         // Note, we actually create the combined matrix here, and save the work
    623         SkMatrix& combinedMatrix = fGeoData[0].fSrcRotInv;
    624         combinedMatrix.postConcat(geometry.fViewMatrix);
    625         combinedMatrix.mapRect(&fBounds);
    626     }
    627 
    628     bool onCombineIfPossible(GrBatch* t) override {
    629         DashBatch* that = t->cast<DashBatch>();
    630 
    631         if (this->aaMode() != that->aaMode()) {
    632             return false;
    633         }
    634 
    635         if (this->fullDash() != that->fullDash()) {
    636             return false;
    637         }
    638 
    639         if (this->cap() != that->cap()) {
    640             return false;
    641         }
    642 
    643         // TODO vertex color
    644         if (this->color() != that->color()) {
    645             return false;
    646         }
    647 
    648         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
    649         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    650             return false;
    651         }
    652 
    653         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
    654         this->joinBounds(that->bounds());
    655         return true;
    656     }
    657 
    658     GrColor color() const { return fBatch.fColor; }
    659     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
    660     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
    661     DashAAMode aaMode() const { return fBatch.fAAMode; }
    662     bool fullDash() const { return fBatch.fFullDash; }
    663     SkPaint::Cap cap() const { return fBatch.fCap; }
    664 
    665     struct BatchTracker {
    666         GrColor fColor;
    667         bool fUsesLocalCoords;
    668         bool fColorIgnored;
    669         bool fCoverageIgnored;
    670         SkPaint::Cap fCap;
    671         DashAAMode fAAMode;
    672         bool fFullDash;
    673     };
    674 
    675     static const int kVertsPerDash = 4;
    676     static const int kIndicesPerDash = 6;
    677 
    678     BatchTracker fBatch;
    679     SkSTArray<1, Geometry, true> fGeoData;
    680 };
    681 
    682 static GrBatch* create_batch(GrColor color, const SkMatrix& viewMatrix, const SkPoint pts[2],
    683                              bool useAA, const GrStrokeInfo& strokeInfo, bool msaaRT) {
    684     const SkScalar* intervals = strokeInfo.getDashIntervals();
    685     SkScalar phase = strokeInfo.getDashPhase();
    686 
    687     SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
    688 
    689     DashBatch::Geometry geometry;
    690     geometry.fSrcStrokeWidth = strokeInfo.getStrokeRec().getWidth();
    691 
    692     // the phase should be normalized to be [0, sum of all intervals)
    693     SkASSERT(phase >= 0 && phase < intervals[0] + intervals[1]);
    694 
    695     // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX
    696     if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
    697         SkMatrix rotMatrix;
    698         align_to_x_axis(pts, &rotMatrix, geometry.fPtsRot);
    699         if(!rotMatrix.invert(&geometry.fSrcRotInv)) {
    700             SkDebugf("Failed to create invertible rotation matrix!\n");
    701             return NULL;
    702         }
    703     } else {
    704         geometry.fSrcRotInv.reset();
    705         memcpy(geometry.fPtsRot, pts, 2 * sizeof(SkPoint));
    706     }
    707 
    708     // Scale corrections of intervals and stroke from view matrix
    709     calc_dash_scaling(&geometry.fParallelScale, &geometry.fPerpendicularScale, viewMatrix,
    710                       geometry.fPtsRot);
    711 
    712     SkScalar offInterval = intervals[1] * geometry.fParallelScale;
    713     SkScalar strokeWidth = geometry.fSrcStrokeWidth * geometry.fPerpendicularScale;
    714 
    715     if (SkPaint::kSquare_Cap == cap && 0 != geometry.fSrcStrokeWidth) {
    716         // add cap to on interveal and remove from off interval
    717         offInterval -= strokeWidth;
    718     }
    719 
    720     DashAAMode aaMode = msaaRT ? kMSAA_DashAAMode :
    721                                  useAA ? kEdgeAA_DashAAMode : kBW_DashAAMode;
    722 
    723     // TODO we can do a real rect call if not using fulldash(ie no off interval, not using AA)
    724     bool fullDash = offInterval > 0.f || aaMode != kBW_DashAAMode;
    725 
    726     geometry.fColor = color;
    727     geometry.fViewMatrix = viewMatrix;
    728     geometry.fPhase = phase;
    729     geometry.fIntervals[0] = intervals[0];
    730     geometry.fIntervals[1] = intervals[1];
    731 
    732     return DashBatch::Create(geometry, cap, aaMode, fullDash);
    733 }
    734 
    735 bool GrDashingEffect::DrawDashLine(GrDrawTarget* target,
    736                                    GrPipelineBuilder* pipelineBuilder, GrColor color,
    737                                    const SkMatrix& viewMatrix, const SkPoint pts[2],
    738                                    bool useAA, const GrStrokeInfo& strokeInfo) {
    739     SkAutoTUnref<GrBatch> batch(create_batch(color, viewMatrix, pts, useAA, strokeInfo,
    740                                              pipelineBuilder->getRenderTarget()->isMultisampled()));
    741     if (!batch) {
    742         return false;
    743     }
    744 
    745     target->drawBatch(pipelineBuilder, batch);
    746     return true;
    747 }
    748 
    749 //////////////////////////////////////////////////////////////////////////////
    750 
    751 class GLDashingCircleEffect;
    752 
    753 struct DashingCircleBatchTracker {
    754     GrGPInput fInputColorType;
    755     GrColor fColor;
    756     bool fUsesLocalCoords;
    757 };
    758 
    759 /*
    760  * This effect will draw a dotted line (defined as a dashed lined with round caps and no on
    761  * interval). The radius of the dots is given by the strokeWidth and the spacing by the DashInfo.
    762  * Both of the previous two parameters are in device space. This effect also requires the setting of
    763  * a vec2 vertex attribute for the the four corners of the bounding rect. This attribute is the
    764  * "dash position" of each vertex. In other words it is the vertex coords (in device space) if we
    765  * transform the line to be horizontal, with the start of line at the origin then shifted to the
    766  * right by half the off interval. The line then goes in the positive x direction.
    767  */
    768 class DashingCircleEffect : public GrGeometryProcessor {
    769 public:
    770     typedef SkPathEffect::DashInfo DashInfo;
    771 
    772     static GrGeometryProcessor* Create(GrColor,
    773                                        DashAAMode aaMode,
    774                                        const SkMatrix& localMatrix);
    775 
    776     const char* name() const override { return "DashingCircleEffect"; }
    777 
    778     const Attribute* inPosition() const { return fInPosition; }
    779 
    780     const Attribute* inDashParams() const { return fInDashParams; }
    781 
    782     const Attribute* inCircleParams() const { return fInCircleParams; }
    783 
    784     DashAAMode aaMode() const { return fAAMode; }
    785 
    786     GrColor color() const { return fColor; }
    787 
    788     const SkMatrix& localMatrix() const { return fLocalMatrix; }
    789 
    790     virtual void getGLProcessorKey(const GrBatchTracker&,
    791                                    const GrGLSLCaps&,
    792                                    GrProcessorKeyBuilder* b) const override;
    793 
    794     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker&,
    795                                                      const GrGLSLCaps&) const override;
    796 
    797     void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override;
    798 
    799 private:
    800     DashingCircleEffect(GrColor, DashAAMode aaMode, const SkMatrix& localMatrix);
    801 
    802     GrColor             fColor;
    803     SkMatrix            fLocalMatrix;
    804     DashAAMode          fAAMode;
    805     const Attribute*    fInPosition;
    806     const Attribute*    fInDashParams;
    807     const Attribute*    fInCircleParams;
    808 
    809     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
    810 
    811     typedef GrGeometryProcessor INHERITED;
    812 };
    813 
    814 //////////////////////////////////////////////////////////////////////////////
    815 
    816 class GLDashingCircleEffect : public GrGLGeometryProcessor {
    817 public:
    818     GLDashingCircleEffect(const GrGeometryProcessor&, const GrBatchTracker&);
    819 
    820     void onEmitCode(EmitArgs&, GrGPArgs*) override;
    821 
    822     static inline void GenKey(const GrGeometryProcessor&,
    823                               const GrBatchTracker&,
    824                               const GrGLSLCaps&,
    825                               GrProcessorKeyBuilder*);
    826 
    827     virtual void setData(const GrGLProgramDataManager&,
    828                          const GrPrimitiveProcessor&,
    829                          const GrBatchTracker&) override;
    830 
    831     void setTransformData(const GrPrimitiveProcessor& primProc,
    832                           const GrGLProgramDataManager& pdman,
    833                           int index,
    834                           const SkTArray<const GrCoordTransform*, true>& transforms) override {
    835         this->setTransformDataHelper<DashingCircleEffect>(primProc, pdman, index, transforms);
    836     }
    837 
    838 private:
    839     UniformHandle fParamUniform;
    840     UniformHandle fColorUniform;
    841     GrColor       fColor;
    842     SkScalar      fPrevRadius;
    843     SkScalar      fPrevCenterX;
    844     SkScalar      fPrevIntervalLength;
    845     typedef GrGLGeometryProcessor INHERITED;
    846 };
    847 
    848 GLDashingCircleEffect::GLDashingCircleEffect(const GrGeometryProcessor&,
    849                                              const GrBatchTracker&) {
    850     fColor = GrColor_ILLEGAL;
    851     fPrevRadius = SK_ScalarMin;
    852     fPrevCenterX = SK_ScalarMin;
    853     fPrevIntervalLength = SK_ScalarMax;
    854 }
    855 
    856 void GLDashingCircleEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
    857     const DashingCircleEffect& dce = args.fGP.cast<DashingCircleEffect>();
    858     const DashingCircleBatchTracker local = args.fBT.cast<DashingCircleBatchTracker>();
    859     GrGLGPBuilder* pb = args.fPB;
    860     GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
    861 
    862     // emit attributes
    863     vsBuilder->emitAttributes(dce);
    864 
    865     // XY are dashPos, Z is dashInterval
    866     GrGLVertToFrag dashParams(kVec3f_GrSLType);
    867     args.fPB->addVarying("DashParam", &dashParams);
    868     vsBuilder->codeAppendf("%s = %s;", dashParams.vsOut(), dce.inDashParams()->fName);
    869 
    870     // x refers to circle radius - 0.5, y refers to cicle's center x coord
    871     GrGLVertToFrag circleParams(kVec2f_GrSLType);
    872     args.fPB->addVarying("CircleParams", &circleParams);
    873     vsBuilder->codeAppendf("%s = %s;", circleParams.vsOut(), dce.inCircleParams()->fName);
    874 
    875     // Setup pass through color
    876     this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL, &fColorUniform);
    877 
    878     // Setup position
    879     this->setupPosition(pb, gpArgs, dce.inPosition()->fName);
    880 
    881     // emit transforms
    882     this->emitTransforms(args.fPB, gpArgs->fPositionVar, dce.inPosition()->fName, dce.localMatrix(),
    883                          args.fTransformsIn, args.fTransformsOut);
    884 
    885     // transforms all points so that we can compare them to our test circle
    886     GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
    887     fsBuilder->codeAppendf("float xShifted = %s.x - floor(%s.x / %s.z) * %s.z;",
    888                            dashParams.fsIn(), dashParams.fsIn(), dashParams.fsIn(),
    889                            dashParams.fsIn());
    890     fsBuilder->codeAppendf("vec2 fragPosShifted = vec2(xShifted, %s.y);", dashParams.fsIn());
    891     fsBuilder->codeAppendf("vec2 center = vec2(%s.y, 0.0);", circleParams.fsIn());
    892     fsBuilder->codeAppend("float dist = length(center - fragPosShifted);");
    893     if (dce.aaMode() != kBW_DashAAMode) {
    894         fsBuilder->codeAppendf("float diff = dist - %s.x;", circleParams.fsIn());
    895         fsBuilder->codeAppend("diff = 1.0 - diff;");
    896         fsBuilder->codeAppend("float alpha = clamp(diff, 0.0, 1.0);");
    897     } else {
    898         fsBuilder->codeAppendf("float alpha = 1.0;");
    899         fsBuilder->codeAppendf("alpha *=  dist < %s.x + 0.5 ? 1.0 : 0.0;", circleParams.fsIn());
    900     }
    901     fsBuilder->codeAppendf("%s = vec4(alpha);", args.fOutputCoverage);
    902 }
    903 
    904 void GLDashingCircleEffect::setData(const GrGLProgramDataManager& pdman,
    905                                     const GrPrimitiveProcessor& processor,
    906                                     const GrBatchTracker& bt) {
    907     const DashingCircleBatchTracker& local = bt.cast<DashingCircleBatchTracker>();
    908     if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
    909         GrGLfloat c[4];
    910         GrColorToRGBAFloat(local.fColor, c);
    911         pdman.set4fv(fColorUniform, 1, c);
    912         fColor = local.fColor;
    913     }
    914 }
    915 
    916 void GLDashingCircleEffect::GenKey(const GrGeometryProcessor& gp,
    917                                    const GrBatchTracker& bt,
    918                                    const GrGLSLCaps&,
    919                                    GrProcessorKeyBuilder* b) {
    920     const DashingCircleBatchTracker& local = bt.cast<DashingCircleBatchTracker>();
    921     const DashingCircleEffect& dce = gp.cast<DashingCircleEffect>();
    922     uint32_t key = 0;
    923     key |= local.fUsesLocalCoords && dce.localMatrix().hasPerspective() ? 0x1 : 0x0;
    924     key |= dce.aaMode() << 8;
    925     b->add32(key << 16 | local.fInputColorType);
    926 }
    927 
    928 //////////////////////////////////////////////////////////////////////////////
    929 
    930 GrGeometryProcessor* DashingCircleEffect::Create(GrColor color,
    931                                                  DashAAMode aaMode,
    932                                                  const SkMatrix& localMatrix) {
    933     return SkNEW_ARGS(DashingCircleEffect, (color, aaMode, localMatrix));
    934 }
    935 
    936 void DashingCircleEffect::getGLProcessorKey(const GrBatchTracker& bt,
    937                                             const GrGLSLCaps& caps,
    938                                             GrProcessorKeyBuilder* b) const {
    939     GLDashingCircleEffect::GenKey(*this, bt, caps, b);
    940 }
    941 
    942 GrGLPrimitiveProcessor* DashingCircleEffect::createGLInstance(const GrBatchTracker& bt,
    943                                                               const GrGLSLCaps&) const {
    944     return SkNEW_ARGS(GLDashingCircleEffect, (*this, bt));
    945 }
    946 
    947 DashingCircleEffect::DashingCircleEffect(GrColor color,
    948                                          DashAAMode aaMode,
    949                                          const SkMatrix& localMatrix)
    950     : fColor(color)
    951     , fLocalMatrix(localMatrix)
    952     , fAAMode(aaMode) {
    953     this->initClassID<DashingCircleEffect>();
    954     fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
    955     fInDashParams = &this->addVertexAttrib(Attribute("inDashParams", kVec3f_GrVertexAttribType));
    956     fInCircleParams = &this->addVertexAttrib(Attribute("inCircleParams",
    957                                                        kVec2f_GrVertexAttribType));
    958 }
    959 
    960 void DashingCircleEffect::initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const {
    961     DashingCircleBatchTracker* local = bt->cast<DashingCircleBatchTracker>();
    962     local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
    963     local->fUsesLocalCoords = init.fUsesLocalCoords;
    964 }
    965 
    966 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingCircleEffect);
    967 
    968 GrGeometryProcessor* DashingCircleEffect::TestCreate(SkRandom* random,
    969                                                      GrContext*,
    970                                                      const GrDrawTargetCaps& caps,
    971                                                      GrTexture*[]) {
    972     DashAAMode aaMode = static_cast<DashAAMode>(random->nextULessThan(kDashAAModeCount));
    973     return DashingCircleEffect::Create(GrRandomColor(random),
    974                                       aaMode, GrTest::TestMatrix(random));
    975 }
    976 
    977 //////////////////////////////////////////////////////////////////////////////
    978 
    979 class GLDashingLineEffect;
    980 
    981 struct DashingLineBatchTracker {
    982     GrGPInput fInputColorType;
    983     GrColor  fColor;
    984     bool fUsesLocalCoords;
    985 };
    986 
    987 /*
    988  * This effect will draw a dashed line. The width of the dash is given by the strokeWidth and the
    989  * length and spacing by the DashInfo. Both of the previous two parameters are in device space.
    990  * This effect also requires the setting of a vec2 vertex attribute for the the four corners of the
    991  * bounding rect. This attribute is the "dash position" of each vertex. In other words it is the
    992  * vertex coords (in device space) if we transform the line to be horizontal, with the start of
    993  * line at the origin then shifted to the right by half the off interval. The line then goes in the
    994  * positive x direction.
    995  */
    996 class DashingLineEffect : public GrGeometryProcessor {
    997 public:
    998     typedef SkPathEffect::DashInfo DashInfo;
    999 
   1000     static GrGeometryProcessor* Create(GrColor,
   1001                                        DashAAMode aaMode,
   1002                                        const SkMatrix& localMatrix);
   1003 
   1004     const char* name() const override { return "DashingEffect"; }
   1005 
   1006     const Attribute* inPosition() const { return fInPosition; }
   1007 
   1008     const Attribute* inDashParams() const { return fInDashParams; }
   1009 
   1010     const Attribute* inRectParams() const { return fInRectParams; }
   1011 
   1012     DashAAMode aaMode() const { return fAAMode; }
   1013 
   1014     GrColor color() const { return fColor; }
   1015 
   1016     const SkMatrix& localMatrix() const { return fLocalMatrix; }
   1017 
   1018     virtual void getGLProcessorKey(const GrBatchTracker& bt,
   1019                                    const GrGLSLCaps& caps,
   1020                                    GrProcessorKeyBuilder* b) const override;
   1021 
   1022     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
   1023                                                      const GrGLSLCaps&) const override;
   1024 
   1025     void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override;
   1026 
   1027 private:
   1028     DashingLineEffect(GrColor, DashAAMode aaMode, const SkMatrix& localMatrix);
   1029 
   1030     GrColor             fColor;
   1031     SkMatrix            fLocalMatrix;
   1032     DashAAMode          fAAMode;
   1033     const Attribute*    fInPosition;
   1034     const Attribute*    fInDashParams;
   1035     const Attribute*    fInRectParams;
   1036 
   1037     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
   1038 
   1039     typedef GrGeometryProcessor INHERITED;
   1040 };
   1041 
   1042 //////////////////////////////////////////////////////////////////////////////
   1043 
   1044 class GLDashingLineEffect : public GrGLGeometryProcessor {
   1045 public:
   1046     GLDashingLineEffect(const GrGeometryProcessor&, const GrBatchTracker&);
   1047 
   1048     void onEmitCode(EmitArgs&, GrGPArgs*) override;
   1049 
   1050     static inline void GenKey(const GrGeometryProcessor&,
   1051                               const GrBatchTracker&,
   1052                               const GrGLSLCaps&,
   1053                               GrProcessorKeyBuilder*);
   1054 
   1055     virtual void setData(const GrGLProgramDataManager&,
   1056                          const GrPrimitiveProcessor&,
   1057                          const GrBatchTracker&) override;
   1058 
   1059     void setTransformData(const GrPrimitiveProcessor& primProc,
   1060                           const GrGLProgramDataManager& pdman,
   1061                           int index,
   1062                           const SkTArray<const GrCoordTransform*, true>& transforms) override {
   1063         this->setTransformDataHelper<DashingLineEffect>(primProc, pdman, index, transforms);
   1064     }
   1065 
   1066 private:
   1067     GrColor       fColor;
   1068     UniformHandle fColorUniform;
   1069     typedef GrGLGeometryProcessor INHERITED;
   1070 };
   1071 
   1072 GLDashingLineEffect::GLDashingLineEffect(const GrGeometryProcessor&,
   1073                                          const GrBatchTracker&) {
   1074     fColor = GrColor_ILLEGAL;
   1075 }
   1076 
   1077 void GLDashingLineEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
   1078     const DashingLineEffect& de = args.fGP.cast<DashingLineEffect>();
   1079     const DashingLineBatchTracker& local = args.fBT.cast<DashingLineBatchTracker>();
   1080     GrGLGPBuilder* pb = args.fPB;
   1081 
   1082     GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
   1083 
   1084     // emit attributes
   1085     vsBuilder->emitAttributes(de);
   1086 
   1087     // XY refers to dashPos, Z is the dash interval length
   1088     GrGLVertToFrag inDashParams(kVec3f_GrSLType);
   1089     args.fPB->addVarying("DashParams", &inDashParams);
   1090     vsBuilder->codeAppendf("%s = %s;", inDashParams.vsOut(), de.inDashParams()->fName);
   1091 
   1092     // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5),
   1093     // respectively.
   1094     GrGLVertToFrag inRectParams(kVec4f_GrSLType);
   1095     args.fPB->addVarying("RectParams", &inRectParams);
   1096     vsBuilder->codeAppendf("%s = %s;", inRectParams.vsOut(), de.inRectParams()->fName);
   1097 
   1098     // Setup pass through color
   1099     this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL, &fColorUniform);
   1100 
   1101     // Setup position
   1102     this->setupPosition(pb, gpArgs, de.inPosition()->fName);
   1103 
   1104     // emit transforms
   1105     this->emitTransforms(args.fPB, gpArgs->fPositionVar, de.inPosition()->fName, de.localMatrix(),
   1106                          args.fTransformsIn, args.fTransformsOut);
   1107 
   1108     // transforms all points so that we can compare them to our test rect
   1109     GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
   1110     fsBuilder->codeAppendf("float xShifted = %s.x - floor(%s.x / %s.z) * %s.z;",
   1111                            inDashParams.fsIn(), inDashParams.fsIn(), inDashParams.fsIn(),
   1112                            inDashParams.fsIn());
   1113     fsBuilder->codeAppendf("vec2 fragPosShifted = vec2(xShifted, %s.y);", inDashParams.fsIn());
   1114     if (de.aaMode() == kEdgeAA_DashAAMode) {
   1115         // The amount of coverage removed in x and y by the edges is computed as a pair of negative
   1116         // numbers, xSub and ySub.
   1117         fsBuilder->codeAppend("float xSub, ySub;");
   1118         fsBuilder->codeAppendf("xSub = min(fragPosShifted.x - %s.x, 0.0);", inRectParams.fsIn());
   1119         fsBuilder->codeAppendf("xSub += min(%s.z - fragPosShifted.x, 0.0);", inRectParams.fsIn());
   1120         fsBuilder->codeAppendf("ySub = min(fragPosShifted.y - %s.y, 0.0);", inRectParams.fsIn());
   1121         fsBuilder->codeAppendf("ySub += min(%s.w - fragPosShifted.y, 0.0);", inRectParams.fsIn());
   1122         // Now compute coverage in x and y and multiply them to get the fraction of the pixel
   1123         // covered.
   1124         fsBuilder->codeAppendf("float alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));");
   1125     } else if (de.aaMode() == kMSAA_DashAAMode) {
   1126         // For MSAA, we don't modulate the alpha by the Y distance, since MSAA coverage will handle
   1127         // AA on the the top and bottom edges. The shader is only responsible for intra-dash alpha.
   1128         fsBuilder->codeAppend("float xSub;");
   1129         fsBuilder->codeAppendf("xSub = min(fragPosShifted.x - %s.x, 0.0);", inRectParams.fsIn());
   1130         fsBuilder->codeAppendf("xSub += min(%s.z - fragPosShifted.x, 0.0);", inRectParams.fsIn());
   1131         // Now compute coverage in x to get the fraction of the pixel covered.
   1132         fsBuilder->codeAppendf("float alpha = (1.0 + max(xSub, -1.0));");
   1133     } else {
   1134         // Assuming the bounding geometry is tight so no need to check y values
   1135         fsBuilder->codeAppendf("float alpha = 1.0;");
   1136         fsBuilder->codeAppendf("alpha *= (fragPosShifted.x - %s.x) > -0.5 ? 1.0 : 0.0;",
   1137                                inRectParams.fsIn());
   1138         fsBuilder->codeAppendf("alpha *= (%s.z - fragPosShifted.x) >= -0.5 ? 1.0 : 0.0;",
   1139                                inRectParams.fsIn());
   1140     }
   1141     fsBuilder->codeAppendf("%s = vec4(alpha);", args.fOutputCoverage);
   1142 }
   1143 
   1144 void GLDashingLineEffect::setData(const GrGLProgramDataManager& pdman,
   1145                                   const GrPrimitiveProcessor& processor,
   1146                                   const GrBatchTracker& bt) {
   1147     const DashingLineBatchTracker& local = bt.cast<DashingLineBatchTracker>();
   1148     if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
   1149         GrGLfloat c[4];
   1150         GrColorToRGBAFloat(local.fColor, c);
   1151         pdman.set4fv(fColorUniform, 1, c);
   1152         fColor = local.fColor;
   1153     }
   1154 }
   1155 
   1156 void GLDashingLineEffect::GenKey(const GrGeometryProcessor& gp,
   1157                                  const GrBatchTracker& bt,
   1158                                  const GrGLSLCaps&,
   1159                                  GrProcessorKeyBuilder* b) {
   1160     const DashingLineBatchTracker& local = bt.cast<DashingLineBatchTracker>();
   1161     const DashingLineEffect& de = gp.cast<DashingLineEffect>();
   1162     uint32_t key = 0;
   1163     key |= local.fUsesLocalCoords && de.localMatrix().hasPerspective() ? 0x1 : 0x0;
   1164     key |= de.aaMode() << 8;
   1165     b->add32(key << 16 | local.fInputColorType);
   1166 }
   1167 
   1168 //////////////////////////////////////////////////////////////////////////////
   1169 
   1170 GrGeometryProcessor* DashingLineEffect::Create(GrColor color,
   1171                                                DashAAMode aaMode,
   1172                                                const SkMatrix& localMatrix) {
   1173     return SkNEW_ARGS(DashingLineEffect, (color, aaMode, localMatrix));
   1174 }
   1175 
   1176 void DashingLineEffect::getGLProcessorKey(const GrBatchTracker& bt,
   1177                                           const GrGLSLCaps& caps,
   1178                                           GrProcessorKeyBuilder* b) const {
   1179     GLDashingLineEffect::GenKey(*this, bt, caps, b);
   1180 }
   1181 
   1182 GrGLPrimitiveProcessor* DashingLineEffect::createGLInstance(const GrBatchTracker& bt,
   1183                                                             const GrGLSLCaps&) const {
   1184     return SkNEW_ARGS(GLDashingLineEffect, (*this, bt));
   1185 }
   1186 
   1187 DashingLineEffect::DashingLineEffect(GrColor color,
   1188                                      DashAAMode aaMode,
   1189                                      const SkMatrix& localMatrix)
   1190     : fColor(color)
   1191     , fLocalMatrix(localMatrix)
   1192     , fAAMode(aaMode) {
   1193     this->initClassID<DashingLineEffect>();
   1194     fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
   1195     fInDashParams = &this->addVertexAttrib(Attribute("inDashParams", kVec3f_GrVertexAttribType));
   1196     fInRectParams = &this->addVertexAttrib(Attribute("inRect", kVec4f_GrVertexAttribType));
   1197 }
   1198 
   1199 void DashingLineEffect::initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const {
   1200     DashingLineBatchTracker* local = bt->cast<DashingLineBatchTracker>();
   1201     local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
   1202     local->fUsesLocalCoords = init.fUsesLocalCoords;
   1203 }
   1204 
   1205 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingLineEffect);
   1206 
   1207 GrGeometryProcessor* DashingLineEffect::TestCreate(SkRandom* random,
   1208                                                    GrContext*,
   1209                                                    const GrDrawTargetCaps& caps,
   1210                                                    GrTexture*[]) {
   1211     DashAAMode aaMode = static_cast<DashAAMode>(random->nextULessThan(kDashAAModeCount));
   1212     return DashingLineEffect::Create(GrRandomColor(random),
   1213                                      aaMode, GrTest::TestMatrix(random));
   1214 }
   1215 
   1216 //////////////////////////////////////////////////////////////////////////////
   1217 
   1218 static GrGeometryProcessor* create_dash_gp(GrColor color,
   1219                                            DashAAMode dashAAMode,
   1220                                            DashCap cap,
   1221                                            const SkMatrix& localMatrix) {
   1222     switch (cap) {
   1223         case kRound_DashCap:
   1224             return DashingCircleEffect::Create(color, dashAAMode, localMatrix);
   1225         case kNonRound_DashCap:
   1226             return DashingLineEffect::Create(color, dashAAMode, localMatrix);
   1227         default:
   1228             SkFAIL("Unexpected dashed cap.");
   1229     }
   1230     return NULL;
   1231 }
   1232 
   1233 /////////////////////////////////////////////////////////////////////////////////////////////////
   1234 
   1235 #ifdef GR_TEST_UTILS
   1236 
   1237 BATCH_TEST_DEFINE(DashBatch) {
   1238     GrColor color = GrRandomColor(random);
   1239     SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
   1240     bool useAA = random->nextBool();
   1241     bool msaaRT = random->nextBool();
   1242 
   1243     // We can only dash either horizontal or vertical lines
   1244     SkPoint pts[2];
   1245     if (random->nextBool()) {
   1246         // vertical
   1247         pts[0].fX = 1.f;
   1248         pts[0].fY = random->nextF() * 10.f;
   1249         pts[1].fX = 1.f;
   1250         pts[1].fY = random->nextF() * 10.f;
   1251     } else {
   1252         // horizontal
   1253         pts[0].fX = random->nextF() * 10.f;
   1254         pts[0].fY = 1.f;
   1255         pts[1].fX = random->nextF() * 10.f;
   1256         pts[1].fY = 1.f;
   1257     }
   1258 
   1259     // pick random cap
   1260     SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::Cap::kCapCount));
   1261 
   1262     SkScalar intervals[2];
   1263 
   1264     // We can only dash with the following intervals
   1265     enum Intervals {
   1266         kOpenOpen_Intervals ,
   1267         kOpenClose_Intervals,
   1268         kCloseOpen_Intervals,
   1269     };
   1270 
   1271     Intervals intervalType = SkPaint::kRound_Cap ?
   1272                              kOpenClose_Intervals :
   1273                              Intervals(random->nextULessThan(kCloseOpen_Intervals + 1));
   1274     static const SkScalar kIntervalMin = 0.1f;
   1275     static const SkScalar kIntervalMax = 10.f;
   1276     switch (intervalType) {
   1277         case kOpenOpen_Intervals:
   1278             intervals[0] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
   1279             intervals[1] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
   1280             break;
   1281         case kOpenClose_Intervals:
   1282             intervals[0] = 0.f;
   1283             intervals[1] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
   1284             break;
   1285         case kCloseOpen_Intervals:
   1286             intervals[0] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
   1287             intervals[1] = 0.f;
   1288             break;
   1289 
   1290     }
   1291 
   1292     // phase is 0 < sum (i0, i1)
   1293     SkScalar phase = random->nextRangeScalar(0, intervals[0] + intervals[1]);
   1294 
   1295     SkPaint p;
   1296     p.setStyle(SkPaint::kStroke_Style);
   1297     p.setStrokeWidth(SkIntToScalar(1));
   1298     p.setStrokeCap(cap);
   1299 
   1300     GrStrokeInfo strokeInfo(p);
   1301 
   1302     SkPathEffect::DashInfo info;
   1303     info.fIntervals = intervals;
   1304     info.fCount = 2;
   1305     info.fPhase = phase;
   1306     SkDEBUGCODE(bool success = ) strokeInfo.setDashInfo(info);
   1307     SkASSERT(success);
   1308 
   1309     return create_batch(color, viewMatrix, pts, useAA, strokeInfo, msaaRT);
   1310 }
   1311 
   1312 #endif
   1313