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