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.
32bool 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
63namespace {
64struct DashLineVertex {
65    SkPoint fPos;
66    SkPoint fDashPos;
67    SkScalar fIntervalLength;
68    SkRect fRect;
69};
70struct DashCircleVertex {
71    SkPoint fPos;
72    SkPoint fDashPos;
73    SkScalar fIntervalLength;
74    SkScalar fRadius;
75    SkScalar fCenterX;
76};
77
78enum DashAAMode {
79    kBW_DashAAMode,
80    kEdgeAA_DashAAMode,
81    kMSAA_DashAAMode,
82
83    kDashAAModeCount,
84};
85};
86
87static 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
107static 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
122static 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
131static 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
155enum DashCap {
156    kRound_DashCap,
157    kNonRound_DashCap,
158};
159
160static int kDashVertices = 4;
161
162template <typename T>
163void 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
183static 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
225static 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 */
241static GrGeometryProcessor* create_dash_gp(GrColor,
242                                           DashAAMode aaMode,
243                                           DashCap cap,
244                                           const SkMatrix& localMatrix);
245
246class DashBatch : public GrBatch {
247public:
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
607private:
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
682static 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
735bool 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
751class GLDashingCircleEffect;
752
753struct 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 */
768class DashingCircleEffect : public GrGeometryProcessor {
769public:
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
799private:
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
816class GLDashingCircleEffect : public GrGLGeometryProcessor {
817public:
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
838private:
839    UniformHandle fParamUniform;
840    UniformHandle fColorUniform;
841    GrColor       fColor;
842    SkScalar      fPrevRadius;
843    SkScalar      fPrevCenterX;
844    SkScalar      fPrevIntervalLength;
845    typedef GrGLGeometryProcessor INHERITED;
846};
847
848GLDashingCircleEffect::GLDashingCircleEffect(const GrGeometryProcessor&,
849                                             const GrBatchTracker&) {
850    fColor = GrColor_ILLEGAL;
851    fPrevRadius = SK_ScalarMin;
852    fPrevCenterX = SK_ScalarMin;
853    fPrevIntervalLength = SK_ScalarMax;
854}
855
856void 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
904void 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
916void 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
930GrGeometryProcessor* DashingCircleEffect::Create(GrColor color,
931                                                 DashAAMode aaMode,
932                                                 const SkMatrix& localMatrix) {
933    return SkNEW_ARGS(DashingCircleEffect, (color, aaMode, localMatrix));
934}
935
936void DashingCircleEffect::getGLProcessorKey(const GrBatchTracker& bt,
937                                            const GrGLSLCaps& caps,
938                                            GrProcessorKeyBuilder* b) const {
939    GLDashingCircleEffect::GenKey(*this, bt, caps, b);
940}
941
942GrGLPrimitiveProcessor* DashingCircleEffect::createGLInstance(const GrBatchTracker& bt,
943                                                              const GrGLSLCaps&) const {
944    return SkNEW_ARGS(GLDashingCircleEffect, (*this, bt));
945}
946
947DashingCircleEffect::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
960void 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
966GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingCircleEffect);
967
968GrGeometryProcessor* 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
979class GLDashingLineEffect;
980
981struct 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 */
996class DashingLineEffect : public GrGeometryProcessor {
997public:
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
1027private:
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
1044class GLDashingLineEffect : public GrGLGeometryProcessor {
1045public:
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
1066private:
1067    GrColor       fColor;
1068    UniformHandle fColorUniform;
1069    typedef GrGLGeometryProcessor INHERITED;
1070};
1071
1072GLDashingLineEffect::GLDashingLineEffect(const GrGeometryProcessor&,
1073                                         const GrBatchTracker&) {
1074    fColor = GrColor_ILLEGAL;
1075}
1076
1077void 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
1144void 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
1156void 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
1170GrGeometryProcessor* DashingLineEffect::Create(GrColor color,
1171                                               DashAAMode aaMode,
1172                                               const SkMatrix& localMatrix) {
1173    return SkNEW_ARGS(DashingLineEffect, (color, aaMode, localMatrix));
1174}
1175
1176void DashingLineEffect::getGLProcessorKey(const GrBatchTracker& bt,
1177                                          const GrGLSLCaps& caps,
1178                                          GrProcessorKeyBuilder* b) const {
1179    GLDashingLineEffect::GenKey(*this, bt, caps, b);
1180}
1181
1182GrGLPrimitiveProcessor* DashingLineEffect::createGLInstance(const GrBatchTracker& bt,
1183                                                            const GrGLSLCaps&) const {
1184    return SkNEW_ARGS(GLDashingLineEffect, (*this, bt));
1185}
1186
1187DashingLineEffect::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
1199void 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
1205GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingLineEffect);
1206
1207GrGeometryProcessor* 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
1218static 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
1237BATCH_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