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