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