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