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