blob: 56f83ca5f462d098eae2c6fe5cf12a94c0e62fb3 [file] [log] [blame]
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/private/GrRecordingContext.h"
9#include "src/core/SkMatrixPriv.h"
10#include "src/core/SkPointPriv.h"
11#include "src/gpu/GrAppliedClip.h"
12#include "src/gpu/GrCaps.h"
13#include "src/gpu/GrCoordTransform.h"
14#include "src/gpu/GrDefaultGeoProcFactory.h"
15#include "src/gpu/GrDrawOpTest.h"
16#include "src/gpu/GrGeometryProcessor.h"
17#include "src/gpu/GrMemoryPool.h"
18#include "src/gpu/GrOpFlushState.h"
19#include "src/gpu/GrProcessor.h"
Robert Phillips345af252020-03-04 10:32:28 -050020#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "src/gpu/GrRecordingContextPriv.h"
22#include "src/gpu/GrStyle.h"
23#include "src/gpu/GrVertexWriter.h"
24#include "src/gpu/SkGr.h"
Michael Ludwigfd4f4df2019-05-29 09:51:09 -040025#include "src/gpu/geometry/GrQuad.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
27#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
28#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
29#include "src/gpu/glsl/GrGLSLUniformHandler.h"
30#include "src/gpu/glsl/GrGLSLVarying.h"
31#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
32#include "src/gpu/ops/GrDashOp.h"
33#include "src/gpu/ops/GrMeshDrawOp.h"
Robert Phillips3968fcb2019-12-05 16:40:31 -050034#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000035
Brian Salomona6aa5902016-12-16 09:32:00 -050036using AAMode = GrDashOp::AAMode;
bsalomonaf18fb42016-06-07 08:10:46 -070037
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000038///////////////////////////////////////////////////////////////////////////////
39
egdaniele61c4112014-06-12 10:24:21 -070040// Returns whether or not the gpu can fast path the dash line effect.
Brian Salomona6aa5902016-12-16 09:32:00 -050041bool GrDashOp::CanDrawDashLine(const SkPoint pts[2], const GrStyle& style,
42 const SkMatrix& viewMatrix) {
egdaniele61c4112014-06-12 10:24:21 -070043 // Pts must be either horizontal or vertical in src space
44 if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) {
45 return false;
46 }
47
48 // May be able to relax this to include skew. As of now cannot do perspective
49 // because of the non uniform scaling of bloating a rect
50 if (!viewMatrix.preservesRightAngles()) {
51 return false;
52 }
53
bsalomon6663acf2016-05-10 09:14:17 -070054 if (!style.isDashed() || 2 != style.dashIntervalCnt()) {
egdaniele61c4112014-06-12 10:24:21 -070055 return false;
56 }
57
bsalomon6663acf2016-05-10 09:14:17 -070058 const SkScalar* intervals = style.dashIntervals();
kkinnunen261694c2015-05-05 08:00:10 -070059 if (0 == intervals[0] && 0 == intervals[1]) {
egdaniele61c4112014-06-12 10:24:21 -070060 return false;
61 }
62
bsalomon6663acf2016-05-10 09:14:17 -070063 SkPaint::Cap cap = style.strokeRec().getCap();
Greg Daniel5fb30562017-06-29 12:27:48 -040064 if (SkPaint::kRound_Cap == cap) {
65 // Current we don't support round caps unless the on interval is zero
66 if (intervals[0] != 0.f) {
67 return false;
68 }
69 // If the width of the circle caps in greater than the off interval we will pick up unwanted
70 // segments of circles at the start and end of the dash line.
71 if (style.strokeRec().getWidth() > intervals[1]) {
72 return false;
73 }
egdaniele61c4112014-06-12 10:24:21 -070074 }
75
76 return true;
77}
78
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000079static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale,
80 const SkMatrix& viewMatrix, const SkPoint pts[2]) {
81 SkVector vecSrc = pts[1] - pts[0];
Greg Danielc96f9b52017-12-07 15:00:06 -050082 if (pts[1] == pts[0]) {
83 vecSrc.set(1.0, 0.0);
84 }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000085 SkScalar magSrc = vecSrc.length();
86 SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0;
87 vecSrc.scale(invSrc);
88
89 SkVector vecSrcPerp;
Cary Clarkdf429f32017-11-08 11:44:31 -050090 SkPointPriv::RotateCW(vecSrc, &vecSrcPerp);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000091 viewMatrix.mapVectors(&vecSrc, 1);
92 viewMatrix.mapVectors(&vecSrcPerp, 1);
93
94 // parallelScale tells how much to scale along the line parallel to the dash line
95 // perpScale tells how much to scale in the direction perpendicular to the dash line
96 *parallelScale = vecSrc.length();
97 *perpScale = vecSrcPerp.length();
98}
99
100// calculates the rotation needed to aligned pts to the x axis with pts[0] < pts[1]
101// Stores the rotation matrix in rotMatrix, and the mapped points in ptsRot
halcanary96fcdcc2015-08-27 07:41:13 -0700102static void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint ptsRot[2] = nullptr) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000103 SkVector vec = pts[1] - pts[0];
Greg Danielc96f9b52017-12-07 15:00:06 -0500104 if (pts[1] == pts[0]) {
105 vec.set(1.0, 0.0);
106 }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000107 SkScalar mag = vec.length();
108 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
109
110 vec.scale(inv);
111 rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
112 if (ptsRot) {
113 rotMatrix->mapPoints(ptsRot, pts, 2);
114 // correction for numerical issues if map doesn't make ptsRot exactly horizontal
115 ptsRot[1].fY = pts[0].fY;
116 }
117}
118
119// Assumes phase < sum of all intervals
joshualitt4f569be2015-02-27 11:41:49 -0800120static SkScalar calc_start_adjustment(const SkScalar intervals[2], SkScalar phase) {
121 SkASSERT(phase < intervals[0] + intervals[1]);
122 if (phase >= intervals[0] && phase != 0) {
123 SkScalar srcIntervalLen = intervals[0] + intervals[1];
124 return srcIntervalLen - phase;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000125 }
126 return 0;
127}
128
joshualitt4f569be2015-02-27 11:41:49 -0800129static SkScalar calc_end_adjustment(const SkScalar intervals[2], const SkPoint pts[2],
egdaniele61c4112014-06-12 10:24:21 -0700130 SkScalar phase, SkScalar* endingInt) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000131 if (pts[1].fX <= pts[0].fX) {
132 return 0;
133 }
joshualitt4f569be2015-02-27 11:41:49 -0800134 SkScalar srcIntervalLen = intervals[0] + intervals[1];
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000135 SkScalar totalLen = pts[1].fX - pts[0].fX;
reed80ea19c2015-05-12 10:37:34 -0700136 SkScalar temp = totalLen / srcIntervalLen;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000137 SkScalar numFullIntervals = SkScalarFloorToScalar(temp);
egdaniele61c4112014-06-12 10:24:21 -0700138 *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase;
reed80ea19c2015-05-12 10:37:34 -0700139 temp = *endingInt / srcIntervalLen;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000140 *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen;
141 if (0 == *endingInt) {
142 *endingInt = srcIntervalLen;
143 }
joshualitt4f569be2015-02-27 11:41:49 -0800144 if (*endingInt > intervals[0]) {
joshualitt4f569be2015-02-27 11:41:49 -0800145 return *endingInt - intervals[0];
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000146 }
147 return 0;
148}
149
joshualitt5224ba72015-02-03 15:07:51 -0800150enum DashCap {
151 kRound_DashCap,
152 kNonRound_DashCap,
153};
154
Brian Osmanb2920f32018-11-16 17:12:40 -0500155static void setup_dashed_rect(const SkRect& rect, GrVertexWriter& vertices, const SkMatrix& matrix,
senorblancof3c2c462015-04-20 14:44:26 -0700156 SkScalar offset, SkScalar bloatX, SkScalar bloatY, SkScalar len,
Brian Osmanb2920f32018-11-16 17:12:40 -0500157 SkScalar stroke, SkScalar startInterval, SkScalar endInterval,
158 SkScalar strokeWidth, DashCap cap) {
joshualitt5224ba72015-02-03 15:07:51 -0800159 SkScalar intervalLength = startInterval + endInterval;
Brian Osmanb2920f32018-11-16 17:12:40 -0500160 SkRect dashRect = { offset - bloatX, -stroke - bloatY,
161 offset + len + bloatX, stroke + bloatY };
joshualitt5224ba72015-02-03 15:07:51 -0800162
163 if (kRound_DashCap == cap) {
joshualitt5224ba72015-02-03 15:07:51 -0800164 SkScalar radius = SkScalarHalf(strokeWidth) - 0.5f;
165 SkScalar centerX = SkScalarHalf(endInterval);
166
Michael Ludwige9c57d32019-02-13 13:39:39 -0500167 vertices.writeQuad(GrQuad::MakeFromRect(rect, matrix),
Brian Osmanb2920f32018-11-16 17:12:40 -0500168 GrVertexWriter::TriStripFromRect(dashRect),
169 intervalLength,
170 radius,
171 centerX);
joshualitt5224ba72015-02-03 15:07:51 -0800172 } else {
Brian Osmanb2920f32018-11-16 17:12:40 -0500173 SkASSERT(kNonRound_DashCap == cap);
joshualitt5224ba72015-02-03 15:07:51 -0800174 SkScalar halfOffLen = SkScalarHalf(endInterval);
175 SkScalar halfStroke = SkScalarHalf(strokeWidth);
176 SkRect rectParam;
Mike Reed92b33352019-08-24 19:39:13 -0400177 rectParam.setLTRB(halfOffLen + 0.5f, -halfStroke + 0.5f,
178 halfOffLen + startInterval - 0.5f, halfStroke - 0.5f);
Brian Osmanb2920f32018-11-16 17:12:40 -0500179
Michael Ludwige9c57d32019-02-13 13:39:39 -0500180 vertices.writeQuad(GrQuad::MakeFromRect(rect, matrix),
Brian Osmanb2920f32018-11-16 17:12:40 -0500181 GrVertexWriter::TriStripFromRect(dashRect),
182 intervalLength,
183 rectParam);
joshualitt5224ba72015-02-03 15:07:51 -0800184 }
egdaniele61c4112014-06-12 10:24:21 -0700185}
186
joshualitt5224ba72015-02-03 15:07:51 -0800187/**
188 * An GrGeometryProcessor that renders a dashed line.
189 * This GrGeometryProcessor is meant for dashed lines that only have a single on/off interval pair.
190 * Bounding geometry is rendered and the effect computes coverage based on the fragment's
191 * position relative to the dashed line.
192 */
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500193static GrGeometryProcessor* make_dash_gp(SkArenaAlloc* arena,
194 const SkPMColor4f&,
195 AAMode aaMode,
196 DashCap cap,
197 const SkMatrix& localMatrix,
198 bool usesLocalCoords);
joshualitt5224ba72015-02-03 15:07:51 -0800199
Brian Salomon98222ac2017-07-12 15:27:54 -0400200class DashOp final : public GrMeshDrawOp {
joshualitt4f569be2015-02-27 11:41:49 -0800201public:
Brian Salomon25a88092016-12-01 09:36:50 -0500202 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400203
Brian Salomona6aa5902016-12-16 09:32:00 -0500204 struct LineData {
joshualitt4f569be2015-02-27 11:41:49 -0800205 SkMatrix fViewMatrix;
206 SkMatrix fSrcRotInv;
207 SkPoint fPtsRot[2];
208 SkScalar fSrcStrokeWidth;
209 SkScalar fPhase;
210 SkScalar fIntervals[2];
211 SkScalar fParallelScale;
212 SkScalar fPerpendicularScale;
joshualitt4f569be2015-02-27 11:41:49 -0800213 };
214
Robert Phillipsb97da532019-02-12 15:24:12 -0500215 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400216 GrPaint&& paint,
217 const LineData& geometry,
218 SkPaint::Cap cap,
219 AAMode aaMode, bool fullDash,
Brian Salomon98222ac2017-07-12 15:27:54 -0400220 const GrUserStencilSettings* stencilSettings) {
Robert Phillips9da87e02019-02-04 13:26:26 -0500221 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400222
223 return pool->allocate<DashOp>(std::move(paint), geometry, cap,
224 aaMode, fullDash, stencilSettings);
joshualitt4f569be2015-02-27 11:41:49 -0800225 }
226
Brian Salomona6aa5902016-12-16 09:32:00 -0500227 const char* name() const override { return "DashOp"; }
joshualitt4f569be2015-02-27 11:41:49 -0800228
Chris Dalton1706cbf2019-05-21 19:35:29 -0600229 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips345af252020-03-04 10:32:28 -0500230 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -0600231 fProgramInfo->visitFPProxies(func);
Robert Phillips345af252020-03-04 10:32:28 -0500232 } else {
233 fProcessorSet.visitProxies(func);
234 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400235 }
236
Brian Osman9a390ac2018-11-12 09:47:48 -0500237#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -0500238 SkString dumpInfo() const override {
239 SkString string;
Brian Salomona6aa5902016-12-16 09:32:00 -0500240 for (const auto& geo : fLines) {
Brian Salomon7c3e7182016-12-01 09:35:30 -0500241 string.appendf("Pt0: [%.2f, %.2f], Pt1: [%.2f, %.2f], Width: %.2f, Ival0: %.2f, "
242 "Ival1 : %.2f, Phase: %.2f\n",
243 geo.fPtsRot[0].fX, geo.fPtsRot[0].fY,
244 geo.fPtsRot[1].fX, geo.fPtsRot[1].fY,
245 geo.fSrcStrokeWidth,
246 geo.fIntervals[0],
247 geo.fIntervals[1],
248 geo.fPhase);
249 }
Brian Salomon98222ac2017-07-12 15:27:54 -0400250 string += fProcessorSet.dumpProcessors();
251 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500252 return string;
253 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500254#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -0500255
Brian Salomon98222ac2017-07-12 15:27:54 -0400256 FixedFunctionFlags fixedFunctionFlags() const override {
257 FixedFunctionFlags flags = FixedFunctionFlags::kNone;
258 if (AAMode::kCoverageWithMSAA == fAAMode) {
259 flags |= FixedFunctionFlags::kUsesHWAA;
260 }
Brian Salomon89717fc2017-07-13 11:25:18 -0400261 if (fStencilSettings != &GrUserStencilSettings::kUnused) {
Brian Salomon98222ac2017-07-12 15:27:54 -0400262 flags |= FixedFunctionFlags::kUsesStencil;
263 }
264 return flags;
265 }
266
Chris Daltonb8fff0d2019-03-05 10:11:58 -0700267 GrProcessorSet::Analysis finalize(
Chris Dalton6ce447a2019-06-23 18:07:38 -0600268 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
Brian Osman5ced0bf2019-03-15 10:15:29 -0400269 GrClampType clampType) override {
Greg Danielf6d60d32020-01-08 13:39:16 -0500270 GrProcessorAnalysisCoverage coverage = GrProcessorAnalysisCoverage::kSingleChannel;
Chris Daltonb8fff0d2019-03-05 10:11:58 -0700271 auto analysis = fProcessorSet.finalize(
Chris Dalton6ce447a2019-06-23 18:07:38 -0600272 fColor, coverage, clip, fStencilSettings, hasMixedSampledCoverage, caps, clampType,
273 &fColor);
Brian Salomon98222ac2017-07-12 15:27:54 -0400274 fUsesLocalCoords = analysis.usesLocalCoords();
Chris Dalton4b62aed2019-01-15 11:53:00 -0700275 return analysis;
Brian Salomon98222ac2017-07-12 15:27:54 -0400276 }
277
bsalomone46f9fe2015-08-18 06:05:14 -0700278private:
Robert Phillips7c525e62018-06-12 10:11:12 -0400279 friend class GrOpMemoryPool; // for ctor
280
Brian Salomon98222ac2017-07-12 15:27:54 -0400281 DashOp(GrPaint&& paint, const LineData& geometry, SkPaint::Cap cap, AAMode aaMode,
282 bool fullDash, const GrUserStencilSettings* stencilSettings)
283 : INHERITED(ClassID())
Brian Osmancf860852018-10-31 14:04:39 -0400284 , fColor(paint.getColor4f())
Brian Salomon98222ac2017-07-12 15:27:54 -0400285 , fFullDash(fullDash)
Brian Salomon89717fc2017-07-13 11:25:18 -0400286 , fCap(cap)
Brian Salomon98222ac2017-07-12 15:27:54 -0400287 , fAAMode(aaMode)
288 , fProcessorSet(std::move(paint))
289 , fStencilSettings(stencilSettings) {
Brian Salomona6aa5902016-12-16 09:32:00 -0500290 fLines.push_back(geometry);
bsalomone46f9fe2015-08-18 06:05:14 -0700291
292 // compute bounds
293 SkScalar halfStrokeWidth = 0.5f * geometry.fSrcStrokeWidth;
294 SkScalar xBloat = SkPaint::kButt_Cap == cap ? 0 : halfStrokeWidth;
bsalomon88cf17d2016-07-08 06:40:56 -0700295 SkRect bounds;
296 bounds.set(geometry.fPtsRot[0], geometry.fPtsRot[1]);
297 bounds.outset(xBloat, halfStrokeWidth);
bsalomone46f9fe2015-08-18 06:05:14 -0700298
299 // Note, we actually create the combined matrix here, and save the work
Brian Salomona6aa5902016-12-16 09:32:00 -0500300 SkMatrix& combinedMatrix = fLines[0].fSrcRotInv;
bsalomone46f9fe2015-08-18 06:05:14 -0700301 combinedMatrix.postConcat(geometry.fViewMatrix);
bsalomon88cf17d2016-07-08 06:40:56 -0700302
Greg Daniel5faf4742019-10-01 15:14:44 -0400303 IsHairline zeroArea = geometry.fSrcStrokeWidth ? IsHairline::kNo
304 : IsHairline::kYes;
bsalomon88cf17d2016-07-08 06:40:56 -0700305 HasAABloat aaBloat = (aaMode == AAMode::kNone) ? HasAABloat ::kNo : HasAABloat::kYes;
306 this->setTransformedBounds(bounds, combinedMatrix, aaBloat, zeroArea);
bsalomone46f9fe2015-08-18 06:05:14 -0700307 }
308
joshualitt4f569be2015-02-27 11:41:49 -0800309 struct DashDraw {
Brian Salomona6aa5902016-12-16 09:32:00 -0500310 DashDraw(const LineData& geo) {
joshualitt144c3c82015-11-30 12:30:13 -0800311 memcpy(fPtsRot, geo.fPtsRot, sizeof(geo.fPtsRot));
312 memcpy(fIntervals, geo.fIntervals, sizeof(geo.fIntervals));
313 fPhase = geo.fPhase;
314 }
315 SkPoint fPtsRot[2];
316 SkScalar fIntervals[2];
317 SkScalar fPhase;
joshualitt4f569be2015-02-27 11:41:49 -0800318 SkScalar fStartOffset;
319 SkScalar fStrokeWidth;
320 SkScalar fLineLength;
321 SkScalar fHalfDevStroke;
senorblancof3c2c462015-04-20 14:44:26 -0700322 SkScalar fDevBloatX;
323 SkScalar fDevBloatY;
joshualitt4f569be2015-02-27 11:41:49 -0800324 bool fLineDone;
325 bool fHasStartRect;
326 bool fHasEndRect;
327 };
328
Robert Phillips2669a7b2020-03-12 12:07:19 -0400329 GrProgramInfo* programInfo() override { return fProgramInfo; }
330
Robert Phillips4133dc42020-03-11 15:55:55 -0400331 void onCreateProgramInfo(const GrCaps* caps,
332 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -0400333 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -0400334 GrAppliedClip&& appliedClip,
335 const GrXferProcessor::DstProxyView& dstProxyView) override {
Robert Phillips345af252020-03-04 10:32:28 -0500336
337 DashCap capType = (this->cap() == SkPaint::kRound_Cap) ? kRound_DashCap : kNonRound_DashCap;
joshualittdf0c5572015-08-03 11:35:28 -0700338
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500339 GrGeometryProcessor* gp;
joshualitt4f569be2015-02-27 11:41:49 -0800340 if (this->fullDash()) {
Robert Phillips345af252020-03-04 10:32:28 -0500341 gp = make_dash_gp(arena, this->color(), this->aaMode(), capType,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500342 this->viewMatrix(), fUsesLocalCoords);
joshualitt4f569be2015-02-27 11:41:49 -0800343 } else {
344 // Set up the vertex data for the line and start/end dashes
joshualittdf0c5572015-08-03 11:35:28 -0700345 using namespace GrDefaultGeoProcFactory;
346 Color color(this->color());
Brian Salomon98222ac2017-07-12 15:27:54 -0400347 LocalCoords::Type localCoordsType =
348 fUsesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
Robert Phillips345af252020-03-04 10:32:28 -0500349 gp = MakeForDeviceSpace(arena,
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400350 color,
351 Coverage::kSolid_Type,
352 localCoordsType,
Brian Salomon8c852be2017-01-04 10:44:42 -0500353 this->viewMatrix());
joshualittdf0c5572015-08-03 11:35:28 -0700354 }
355
356 if (!gp) {
357 SkDebugf("Could not create GrGeometryProcessor\n");
Robert Phillips4133dc42020-03-11 15:55:55 -0400358 return;
Robert Phillips345af252020-03-04 10:32:28 -0500359 }
360
361 auto pipelineFlags = GrPipeline::InputFlags::kNone;
362 if (AAMode::kCoverageWithMSAA == fAAMode) {
363 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
364 }
365
Robert Phillips4133dc42020-03-11 15:55:55 -0400366 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps,
367 arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -0400368 writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -0400369 std::move(appliedClip),
370 dstProxyView,
371 gp,
372 std::move(fProcessorSet),
373 GrPrimitiveType::kTriangles,
374 pipelineFlags,
375 fStencilSettings);
Robert Phillips345af252020-03-04 10:32:28 -0500376 }
377
Robert Phillips345af252020-03-04 10:32:28 -0500378 void onPrepareDraws(Target* target) override {
379 int instanceCount = fLines.count();
380 SkPaint::Cap cap = this->cap();
381 DashCap capType = (SkPaint::kRound_Cap == cap) ? kRound_DashCap : kNonRound_DashCap;
382
383 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -0400384 this->createProgramInfo(target);
Robert Phillips345af252020-03-04 10:32:28 -0500385 if (!fProgramInfo) {
386 return;
387 }
joshualitt4f569be2015-02-27 11:41:49 -0800388 }
389
senorblancof3c2c462015-04-20 14:44:26 -0700390 // useAA here means Edge AA or MSAA
bsalomonaf18fb42016-06-07 08:10:46 -0700391 bool useAA = this->aaMode() != AAMode::kNone;
joshualitt4f569be2015-02-27 11:41:49 -0800392 bool fullDash = this->fullDash();
393
394 // We do two passes over all of the dashes. First we setup the start, end, and bounds,
395 // rectangles. We preserve all of this work in the rects / draws arrays below. Then we
396 // iterate again over these decomposed dashes to generate vertices
joshualitt144c3c82015-11-30 12:30:13 -0800397 static const int kNumStackDashes = 128;
398 SkSTArray<kNumStackDashes, SkRect, true> rects;
399 SkSTArray<kNumStackDashes, DashDraw, true> draws;
joshualitt4f569be2015-02-27 11:41:49 -0800400
401 int totalRectCount = 0;
joshualittd0f54572015-03-02 12:00:52 -0800402 int rectOffset = 0;
bsalomonb5238a72015-05-05 07:49:49 -0700403 rects.push_back_n(3 * instanceCount);
joshualitt4f569be2015-02-27 11:41:49 -0800404 for (int i = 0; i < instanceCount; i++) {
Brian Salomona6aa5902016-12-16 09:32:00 -0500405 const LineData& args = fLines[i];
joshualitt144c3c82015-11-30 12:30:13 -0800406
407 DashDraw& draw = draws.push_back(args);
joshualitt4f569be2015-02-27 11:41:49 -0800408
Greg Daniel95581bb2017-06-30 10:36:38 -0400409 bool hasCap = SkPaint::kButt_Cap != cap;
joshualitt4f569be2015-02-27 11:41:49 -0800410
411 // We always want to at least stroke out half a pixel on each side in device space
412 // so 0.5f / perpScale gives us this min in src space
413 SkScalar halfSrcStroke =
Brian Osman116b33e2020-02-05 13:34:09 -0500414 std::max(args.fSrcStrokeWidth * 0.5f, 0.5f / args.fPerpendicularScale);
joshualitt4f569be2015-02-27 11:41:49 -0800415
416 SkScalar strokeAdj;
417 if (!hasCap) {
418 strokeAdj = 0.f;
419 } else {
420 strokeAdj = halfSrcStroke;
421 }
422
423 SkScalar startAdj = 0;
424
joshualitt4f569be2015-02-27 11:41:49 -0800425 bool lineDone = false;
426
427 // Too simplify the algorithm, we always push back rects for start and end rect.
428 // Otherwise we'd have to track start / end rects for each individual geometry
joshualittd0f54572015-03-02 12:00:52 -0800429 SkRect& bounds = rects[rectOffset++];
430 SkRect& startRect = rects[rectOffset++];
431 SkRect& endRect = rects[rectOffset++];
joshualitt4f569be2015-02-27 11:41:49 -0800432
433 bool hasStartRect = false;
434 // If we are using AA, check to see if we are drawing a partial dash at the start. If so
435 // draw it separately here and adjust our start point accordingly
436 if (useAA) {
joshualitt144c3c82015-11-30 12:30:13 -0800437 if (draw.fPhase > 0 && draw.fPhase < draw.fIntervals[0]) {
joshualitt4f569be2015-02-27 11:41:49 -0800438 SkPoint startPts[2];
joshualitt144c3c82015-11-30 12:30:13 -0800439 startPts[0] = draw.fPtsRot[0];
joshualitt4f569be2015-02-27 11:41:49 -0800440 startPts[1].fY = startPts[0].fY;
Brian Osman116b33e2020-02-05 13:34:09 -0500441 startPts[1].fX = std::min(startPts[0].fX + draw.fIntervals[0] - draw.fPhase,
442 draw.fPtsRot[1].fX);
Mike Reed92b33352019-08-24 19:39:13 -0400443 startRect.setBounds(startPts, 2);
joshualitt4f569be2015-02-27 11:41:49 -0800444 startRect.outset(strokeAdj, halfSrcStroke);
445
446 hasStartRect = true;
joshualitt144c3c82015-11-30 12:30:13 -0800447 startAdj = draw.fIntervals[0] + draw.fIntervals[1] - draw.fPhase;
joshualitt4f569be2015-02-27 11:41:49 -0800448 }
449 }
450
451 // adjustments for start and end of bounding rect so we only draw dash intervals
452 // contained in the original line segment.
joshualitt144c3c82015-11-30 12:30:13 -0800453 startAdj += calc_start_adjustment(draw.fIntervals, draw.fPhase);
joshualitt4f569be2015-02-27 11:41:49 -0800454 if (startAdj != 0) {
joshualitt144c3c82015-11-30 12:30:13 -0800455 draw.fPtsRot[0].fX += startAdj;
456 draw.fPhase = 0;
joshualitt4f569be2015-02-27 11:41:49 -0800457 }
458 SkScalar endingInterval = 0;
joshualitt144c3c82015-11-30 12:30:13 -0800459 SkScalar endAdj = calc_end_adjustment(draw.fIntervals, draw.fPtsRot, draw.fPhase,
joshualitt4f569be2015-02-27 11:41:49 -0800460 &endingInterval);
joshualitt144c3c82015-11-30 12:30:13 -0800461 draw.fPtsRot[1].fX -= endAdj;
462 if (draw.fPtsRot[0].fX >= draw.fPtsRot[1].fX) {
joshualitt4f569be2015-02-27 11:41:49 -0800463 lineDone = true;
464 }
465
466 bool hasEndRect = false;
467 // If we are using AA, check to see if we are drawing a partial dash at then end. If so
468 // draw it separately here and adjust our end point accordingly
469 if (useAA && !lineDone) {
470 // If we adjusted the end then we will not be drawing a partial dash at the end.
471 // If we didn't adjust the end point then we just need to make sure the ending
472 // dash isn't a full dash
joshualitt144c3c82015-11-30 12:30:13 -0800473 if (0 == endAdj && endingInterval != draw.fIntervals[0]) {
joshualitt4f569be2015-02-27 11:41:49 -0800474 SkPoint endPts[2];
joshualitt144c3c82015-11-30 12:30:13 -0800475 endPts[1] = draw.fPtsRot[1];
joshualitt4f569be2015-02-27 11:41:49 -0800476 endPts[0].fY = endPts[1].fY;
477 endPts[0].fX = endPts[1].fX - endingInterval;
478
Mike Reed92b33352019-08-24 19:39:13 -0400479 endRect.setBounds(endPts, 2);
joshualitt4f569be2015-02-27 11:41:49 -0800480 endRect.outset(strokeAdj, halfSrcStroke);
481
482 hasEndRect = true;
joshualitt144c3c82015-11-30 12:30:13 -0800483 endAdj = endingInterval + draw.fIntervals[1];
joshualitt4f569be2015-02-27 11:41:49 -0800484
joshualitt144c3c82015-11-30 12:30:13 -0800485 draw.fPtsRot[1].fX -= endAdj;
486 if (draw.fPtsRot[0].fX >= draw.fPtsRot[1].fX) {
joshualitt4f569be2015-02-27 11:41:49 -0800487 lineDone = true;
488 }
489 }
490 }
491
Greg Danielc96f9b52017-12-07 15:00:06 -0500492 if (draw.fPtsRot[0].fX == draw.fPtsRot[1].fX &&
493 (0 != endAdj || 0 == startAdj) &&
494 hasCap) {
495 // At this point the fPtsRot[0]/[1] represent the start and end of the inner rect of
496 // dashes that we want to draw. The only way they can be equal is if the on interval
497 // is zero (or an edge case if the end of line ends at a full off interval, but this
498 // is handled as well). Thus if the on interval is zero then we need to draw a cap
499 // at this position if the stroke has caps. The spec says we only draw this point if
500 // point lies between [start of line, end of line). Thus we check if we are at the
501 // end (but not the start), and if so we don't draw the cap.
502 lineDone = false;
503 }
504
joshualitt4f569be2015-02-27 11:41:49 -0800505 if (startAdj != 0) {
joshualitt144c3c82015-11-30 12:30:13 -0800506 draw.fPhase = 0;
joshualitt4f569be2015-02-27 11:41:49 -0800507 }
508
509 // Change the dashing info from src space into device space
joshualitt144c3c82015-11-30 12:30:13 -0800510 SkScalar* devIntervals = draw.fIntervals;
511 devIntervals[0] = draw.fIntervals[0] * args.fParallelScale;
512 devIntervals[1] = draw.fIntervals[1] * args.fParallelScale;
513 SkScalar devPhase = draw.fPhase * args.fParallelScale;
joshualitt4f569be2015-02-27 11:41:49 -0800514 SkScalar strokeWidth = args.fSrcStrokeWidth * args.fPerpendicularScale;
515
senorblancof3c2c462015-04-20 14:44:26 -0700516 if ((strokeWidth < 1.f && useAA) || 0.f == strokeWidth) {
joshualitt4f569be2015-02-27 11:41:49 -0800517 strokeWidth = 1.f;
518 }
519
520 SkScalar halfDevStroke = strokeWidth * 0.5f;
521
Greg Daniel95581bb2017-06-30 10:36:38 -0400522 if (SkPaint::kSquare_Cap == cap) {
joshualitt4f569be2015-02-27 11:41:49 -0800523 // add cap to on interval and remove from off interval
524 devIntervals[0] += strokeWidth;
525 devIntervals[1] -= strokeWidth;
526 }
527 SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
528
senorblancof3c2c462015-04-20 14:44:26 -0700529 // For EdgeAA, we bloat in X & Y for both square and round caps.
530 // For MSAA, we don't bloat at all for square caps, and bloat in Y only for round caps.
bsalomonaf18fb42016-06-07 08:10:46 -0700531 SkScalar devBloatX = this->aaMode() == AAMode::kCoverage ? 0.5f : 0.0f;
532 SkScalar devBloatY;
533 if (SkPaint::kRound_Cap == cap && this->aaMode() == AAMode::kCoverageWithMSAA) {
534 devBloatY = 0.5f;
535 } else {
536 devBloatY = devBloatX;
537 }
joshualitt4f569be2015-02-27 11:41:49 -0800538
senorblancof3c2c462015-04-20 14:44:26 -0700539 SkScalar bloatX = devBloatX / args.fParallelScale;
540 SkScalar bloatY = devBloatY / args.fPerpendicularScale;
joshualitt4f569be2015-02-27 11:41:49 -0800541
542 if (devIntervals[1] <= 0.f && useAA) {
543 // Case when we end up drawing a solid AA rect
544 // Reset the start rect to draw this single solid rect
545 // but it requires to upload a new intervals uniform so we can mimic
546 // one giant dash
joshualitt144c3c82015-11-30 12:30:13 -0800547 draw.fPtsRot[0].fX -= hasStartRect ? startAdj : 0;
548 draw.fPtsRot[1].fX += hasEndRect ? endAdj : 0;
Mike Reed92b33352019-08-24 19:39:13 -0400549 startRect.setBounds(draw.fPtsRot, 2);
joshualitt4f569be2015-02-27 11:41:49 -0800550 startRect.outset(strokeAdj, halfSrcStroke);
551 hasStartRect = true;
552 hasEndRect = false;
553 lineDone = true;
554
555 SkPoint devicePts[2];
joshualitt144c3c82015-11-30 12:30:13 -0800556 args.fViewMatrix.mapPoints(devicePts, draw.fPtsRot, 2);
joshualitt4f569be2015-02-27 11:41:49 -0800557 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
558 if (hasCap) {
559 lineLength += 2.f * halfDevStroke;
560 }
561 devIntervals[0] = lineLength;
562 }
563
564 totalRectCount += !lineDone ? 1 : 0;
565 totalRectCount += hasStartRect ? 1 : 0;
566 totalRectCount += hasEndRect ? 1 : 0;
567
568 if (SkPaint::kRound_Cap == cap && 0 != args.fSrcStrokeWidth) {
569 // need to adjust this for round caps to correctly set the dashPos attrib on
570 // vertices
571 startOffset -= halfDevStroke;
572 }
573
joshualitt4f569be2015-02-27 11:41:49 -0800574 if (!lineDone) {
575 SkPoint devicePts[2];
joshualitt144c3c82015-11-30 12:30:13 -0800576 args.fViewMatrix.mapPoints(devicePts, draw.fPtsRot, 2);
joshualitt4f569be2015-02-27 11:41:49 -0800577 draw.fLineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
578 if (hasCap) {
579 draw.fLineLength += 2.f * halfDevStroke;
580 }
581
Mike Reed92b33352019-08-24 19:39:13 -0400582 bounds.setLTRB(draw.fPtsRot[0].fX, draw.fPtsRot[0].fY,
583 draw.fPtsRot[1].fX, draw.fPtsRot[1].fY);
joshualitt4f569be2015-02-27 11:41:49 -0800584 bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke);
585 }
586
587 if (hasStartRect) {
588 SkASSERT(useAA); // so that we know bloatX and bloatY have been set
589 startRect.outset(bloatX, bloatY);
590 }
591
592 if (hasEndRect) {
593 SkASSERT(useAA); // so that we know bloatX and bloatY have been set
594 endRect.outset(bloatX, bloatY);
595 }
596
597 draw.fStartOffset = startOffset;
senorblancof3c2c462015-04-20 14:44:26 -0700598 draw.fDevBloatX = devBloatX;
599 draw.fDevBloatY = devBloatY;
joshualitt4f569be2015-02-27 11:41:49 -0800600 draw.fHalfDevStroke = halfDevStroke;
601 draw.fStrokeWidth = strokeWidth;
602 draw.fHasStartRect = hasStartRect;
603 draw.fLineDone = lineDone;
604 draw.fHasEndRect = hasEndRect;
605 }
606
bsalomonb5238a72015-05-05 07:49:49 -0700607 if (!totalRectCount) {
608 return;
609 }
bsalomon8415abe2015-05-04 11:41:41 -0700610
Robert Phillips345af252020-03-04 10:32:28 -0500611 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), totalRectCount);
Brian Osmanb2920f32018-11-16 17:12:40 -0500612 GrVertexWriter vertices{ helper.vertices() };
613 if (!vertices.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -0800614 return;
615 }
616
joshualitt4f569be2015-02-27 11:41:49 -0800617 int rectIndex = 0;
618 for (int i = 0; i < instanceCount; i++) {
Brian Salomona6aa5902016-12-16 09:32:00 -0500619 const LineData& geom = fLines[i];
joshualitt4f569be2015-02-27 11:41:49 -0800620
621 if (!draws[i].fLineDone) {
622 if (fullDash) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400623 setup_dashed_rect(
Brian Osmanb2920f32018-11-16 17:12:40 -0500624 rects[rectIndex], vertices, geom.fSrcRotInv,
Brian Salomon92be2f72018-06-19 14:33:47 -0400625 draws[i].fStartOffset, draws[i].fDevBloatX, draws[i].fDevBloatY,
626 draws[i].fLineLength, draws[i].fHalfDevStroke, draws[i].fIntervals[0],
Brian Osmanb2920f32018-11-16 17:12:40 -0500627 draws[i].fIntervals[1], draws[i].fStrokeWidth, capType);
joshualitt4f569be2015-02-27 11:41:49 -0800628 } else {
Michael Ludwige9c57d32019-02-13 13:39:39 -0500629 vertices.writeQuad(GrQuad::MakeFromRect(rects[rectIndex], geom.fSrcRotInv));
joshualitt4f569be2015-02-27 11:41:49 -0800630 }
joshualitt4f569be2015-02-27 11:41:49 -0800631 }
632 rectIndex++;
633
634 if (draws[i].fHasStartRect) {
635 if (fullDash) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400636 setup_dashed_rect(
Brian Osmanb2920f32018-11-16 17:12:40 -0500637 rects[rectIndex], vertices, geom.fSrcRotInv,
Brian Salomon92be2f72018-06-19 14:33:47 -0400638 draws[i].fStartOffset, draws[i].fDevBloatX, draws[i].fDevBloatY,
639 draws[i].fIntervals[0], draws[i].fHalfDevStroke, draws[i].fIntervals[0],
Brian Osmanb2920f32018-11-16 17:12:40 -0500640 draws[i].fIntervals[1], draws[i].fStrokeWidth, capType);
joshualitt4f569be2015-02-27 11:41:49 -0800641 } else {
Michael Ludwige9c57d32019-02-13 13:39:39 -0500642 vertices.writeQuad(GrQuad::MakeFromRect(rects[rectIndex], geom.fSrcRotInv));
joshualitt4f569be2015-02-27 11:41:49 -0800643 }
joshualitt4f569be2015-02-27 11:41:49 -0800644 }
645 rectIndex++;
646
647 if (draws[i].fHasEndRect) {
648 if (fullDash) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400649 setup_dashed_rect(
Brian Osmanb2920f32018-11-16 17:12:40 -0500650 rects[rectIndex], vertices, geom.fSrcRotInv,
Brian Salomon92be2f72018-06-19 14:33:47 -0400651 draws[i].fStartOffset, draws[i].fDevBloatX, draws[i].fDevBloatY,
652 draws[i].fIntervals[0], draws[i].fHalfDevStroke, draws[i].fIntervals[0],
Brian Osmanb2920f32018-11-16 17:12:40 -0500653 draws[i].fIntervals[1], draws[i].fStrokeWidth, capType);
joshualitt4f569be2015-02-27 11:41:49 -0800654 } else {
Michael Ludwige9c57d32019-02-13 13:39:39 -0500655 vertices.writeQuad(GrQuad::MakeFromRect(rects[rectIndex], geom.fSrcRotInv));
joshualitt4f569be2015-02-27 11:41:49 -0800656 }
joshualitt4f569be2015-02-27 11:41:49 -0800657 }
658 rectIndex++;
659 }
Robert Phillips345af252020-03-04 10:32:28 -0500660
661 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700662 }
663
664 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips345af252020-03-04 10:32:28 -0500665 if (!fProgramInfo || !fMesh) {
666 return;
Brian Salomon98222ac2017-07-12 15:27:54 -0400667 }
Robert Phillips3968fcb2019-12-05 16:40:31 -0500668
Chris Dalton765ed362020-03-16 17:34:44 -0600669 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
670 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
671 flushState->drawMesh(*fMesh);
joshualitt4f569be2015-02-27 11:41:49 -0800672 }
673
Michael Ludwig28b0c5d2019-12-19 14:51:00 -0500674 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
675 const GrCaps& caps) override {
Brian Salomona6aa5902016-12-16 09:32:00 -0500676 DashOp* that = t->cast<DashOp>();
Brian Salomon98222ac2017-07-12 15:27:54 -0400677 if (fProcessorSet != that->fProcessorSet) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000678 return CombineResult::kCannotCombine;
Brian Salomon98222ac2017-07-12 15:27:54 -0400679 }
joshualitt8cab9a72015-07-16 09:13:50 -0700680
senorblancof3c2c462015-04-20 14:44:26 -0700681 if (this->aaMode() != that->aaMode()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000682 return CombineResult::kCannotCombine;
joshualitt4f569be2015-02-27 11:41:49 -0800683 }
684
685 if (this->fullDash() != that->fullDash()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000686 return CombineResult::kCannotCombine;
joshualitt4f569be2015-02-27 11:41:49 -0800687 }
688
689 if (this->cap() != that->cap()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000690 return CombineResult::kCannotCombine;
joshualitt4f569be2015-02-27 11:41:49 -0800691 }
692
693 // TODO vertex color
694 if (this->color() != that->color()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000695 return CombineResult::kCannotCombine;
joshualitt4f569be2015-02-27 11:41:49 -0800696 }
697
Mike Reed2c383152019-12-18 16:47:47 -0500698 if (fUsesLocalCoords && !SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000699 return CombineResult::kCannotCombine;
joshualitt4f569be2015-02-27 11:41:49 -0800700 }
701
Brian Salomona6aa5902016-12-16 09:32:00 -0500702 fLines.push_back_n(that->fLines.count(), that->fLines.begin());
Brian Salomon7eae3e02018-08-07 14:02:38 +0000703 return CombineResult::kMerged;
joshualitt4f569be2015-02-27 11:41:49 -0800704 }
705
Brian Osmancf860852018-10-31 14:04:39 -0400706 const SkPMColor4f& color() const { return fColor; }
Brian Salomona6aa5902016-12-16 09:32:00 -0500707 const SkMatrix& viewMatrix() const { return fLines[0].fViewMatrix; }
708 AAMode aaMode() const { return fAAMode; }
709 bool fullDash() const { return fFullDash; }
710 SkPaint::Cap cap() const { return fCap; }
joshualitt4f569be2015-02-27 11:41:49 -0800711
712 static const int kVertsPerDash = 4;
713 static const int kIndicesPerDash = 6;
714
Brian Salomonbeae8a92017-07-12 16:58:54 +0000715 SkSTArray<1, LineData, true> fLines;
Brian Osmancf860852018-10-31 14:04:39 -0400716 SkPMColor4f fColor;
Brian Salomon98222ac2017-07-12 15:27:54 -0400717 bool fUsesLocalCoords : 1;
Brian Salomon98222ac2017-07-12 15:27:54 -0400718 bool fFullDash : 1;
Brian Salomon89717fc2017-07-13 11:25:18 -0400719 // We use 3 bits for this 3-value enum because MSVS makes the underlying types signed.
720 SkPaint::Cap fCap : 3;
Brian Salomon98222ac2017-07-12 15:27:54 -0400721 AAMode fAAMode;
722 GrProcessorSet fProcessorSet;
723 const GrUserStencilSettings* fStencilSettings;
reed1b55a962015-09-17 20:16:13 -0700724
Chris Daltoneb694b72020-03-16 09:25:50 -0600725 GrSimpleMesh* fMesh = nullptr;
Robert Phillips345af252020-03-04 10:32:28 -0500726 GrProgramInfo* fProgramInfo = nullptr;
727
Brian Salomon98222ac2017-07-12 15:27:54 -0400728 typedef GrMeshDrawOp INHERITED;
joshualitt4f569be2015-02-27 11:41:49 -0800729};
730
Robert Phillipsb97da532019-02-12 15:24:12 -0500731std::unique_ptr<GrDrawOp> GrDashOp::MakeDashLineOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400732 GrPaint&& paint,
Brian Salomon98222ac2017-07-12 15:27:54 -0400733 const SkMatrix& viewMatrix,
734 const SkPoint pts[2],
735 AAMode aaMode,
736 const GrStyle& style,
737 const GrUserStencilSettings* stencilSettings) {
Brian Salomona6aa5902016-12-16 09:32:00 -0500738 SkASSERT(GrDashOp::CanDrawDashLine(pts, style, viewMatrix));
bsalomon6663acf2016-05-10 09:14:17 -0700739 const SkScalar* intervals = style.dashIntervals();
740 SkScalar phase = style.dashPhase();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000741
bsalomon6663acf2016-05-10 09:14:17 -0700742 SkPaint::Cap cap = style.strokeRec().getCap();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000743
Brian Salomona6aa5902016-12-16 09:32:00 -0500744 DashOp::LineData lineData;
745 lineData.fSrcStrokeWidth = style.strokeRec().getWidth();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000746
747 // the phase should be normalized to be [0, sum of all intervals)
kkinnunen261694c2015-05-05 08:00:10 -0700748 SkASSERT(phase >= 0 && phase < intervals[0] + intervals[1]);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000749
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000750 // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000751 if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
egdaniele61c4112014-06-12 10:24:21 -0700752 SkMatrix rotMatrix;
Brian Salomona6aa5902016-12-16 09:32:00 -0500753 align_to_x_axis(pts, &rotMatrix, lineData.fPtsRot);
754 if (!rotMatrix.invert(&lineData.fSrcRotInv)) {
tfarina38406c82014-10-31 07:11:12 -0700755 SkDebugf("Failed to create invertible rotation matrix!\n");
halcanary96fcdcc2015-08-27 07:41:13 -0700756 return nullptr;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000757 }
758 } else {
Brian Salomona6aa5902016-12-16 09:32:00 -0500759 lineData.fSrcRotInv.reset();
760 memcpy(lineData.fPtsRot, pts, 2 * sizeof(SkPoint));
joshualitt4f569be2015-02-27 11:41:49 -0800761 }
762
763 // Scale corrections of intervals and stroke from view matrix
Brian Salomona6aa5902016-12-16 09:32:00 -0500764 calc_dash_scaling(&lineData.fParallelScale, &lineData.fPerpendicularScale, viewMatrix,
765 lineData.fPtsRot);
Jim Van Verthf86073a2018-10-02 13:05:38 -0400766 if (SkScalarNearlyZero(lineData.fParallelScale) ||
767 SkScalarNearlyZero(lineData.fPerpendicularScale)) {
768 return nullptr;
769 }
joshualitt4f569be2015-02-27 11:41:49 -0800770
Brian Salomona6aa5902016-12-16 09:32:00 -0500771 SkScalar offInterval = intervals[1] * lineData.fParallelScale;
772 SkScalar strokeWidth = lineData.fSrcStrokeWidth * lineData.fPerpendicularScale;
joshualitt4f569be2015-02-27 11:41:49 -0800773
Brian Salomona6aa5902016-12-16 09:32:00 -0500774 if (SkPaint::kSquare_Cap == cap && 0 != lineData.fSrcStrokeWidth) {
joshualitt4f569be2015-02-27 11:41:49 -0800775 // add cap to on interveal and remove from off interval
776 offInterval -= strokeWidth;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000777 }
778
joshualitt4f569be2015-02-27 11:41:49 -0800779 // TODO we can do a real rect call if not using fulldash(ie no off interval, not using AA)
bsalomonaf18fb42016-06-07 08:10:46 -0700780 bool fullDash = offInterval > 0.f || aaMode != AAMode::kNone;
skia.committer@gmail.com3b9e8be2014-05-20 03:05:34 +0000781
Brian Salomona6aa5902016-12-16 09:32:00 -0500782 lineData.fViewMatrix = viewMatrix;
783 lineData.fPhase = phase;
784 lineData.fIntervals[0] = intervals[0];
785 lineData.fIntervals[1] = intervals[1];
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000786
Robert Phillips7c525e62018-06-12 10:11:12 -0400787 return DashOp::Make(context, std::move(paint), lineData, cap, aaMode, fullDash,
788 stencilSettings);
joshualittfa2008f2015-04-29 11:32:05 -0700789}
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000790
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000791//////////////////////////////////////////////////////////////////////////////
792
egdanielf767e792014-07-02 06:21:32 -0700793class GLDashingCircleEffect;
joshualitt9b989322014-12-15 14:16:27 -0800794
egdanielf767e792014-07-02 06:21:32 -0700795/*
796 * This effect will draw a dotted line (defined as a dashed lined with round caps and no on
797 * interval). The radius of the dots is given by the strokeWidth and the spacing by the DashInfo.
798 * Both of the previous two parameters are in device space. This effect also requires the setting of
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400799 * a float2 vertex attribute for the the four corners of the bounding rect. This attribute is the
egdanielf767e792014-07-02 06:21:32 -0700800 * "dash position" of each vertex. In other words it is the vertex coords (in device space) if we
801 * transform the line to be horizontal, with the start of line at the origin then shifted to the
802 * right by half the off interval. The line then goes in the positive x direction.
803 */
joshualitt249af152014-09-15 11:41:13 -0700804class DashingCircleEffect : public GrGeometryProcessor {
egdanielf767e792014-07-02 06:21:32 -0700805public:
806 typedef SkPathEffect::DashInfo DashInfo;
807
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500808 static GrGeometryProcessor* Make(SkArenaAlloc* arena,
809 const SkPMColor4f&,
810 AAMode aaMode,
811 const SkMatrix& localMatrix,
812 bool usesLocalCoords);
egdanielf767e792014-07-02 06:21:32 -0700813
mtklein36352bf2015-03-25 18:17:31 -0700814 const char* name() const override { return "DashingCircleEffect"; }
egdanielf767e792014-07-02 06:21:32 -0700815
bsalomonaf18fb42016-06-07 08:10:46 -0700816 AAMode aaMode() const { return fAAMode; }
egdanielf767e792014-07-02 06:21:32 -0700817
Brian Osmancf860852018-10-31 14:04:39 -0400818 const SkPMColor4f& color() const { return fColor; }
joshualitt88c23fc2015-05-13 14:18:07 -0700819
joshualitte3ababe2015-05-15 07:56:07 -0700820 const SkMatrix& localMatrix() const { return fLocalMatrix; }
821
joshualittb8c241a2015-05-19 08:23:30 -0700822 bool usesLocalCoords() const { return fUsesLocalCoords; }
823
Brian Salomon94efbf52016-11-29 13:43:05 -0500824 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override;
egdanielf767e792014-07-02 06:21:32 -0700825
Brian Salomon94efbf52016-11-29 13:43:05 -0500826 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
egdanielf767e792014-07-02 06:21:32 -0700827
828private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500829 friend class GLDashingCircleEffect;
830 friend class ::SkArenaAlloc; // for access to ctor
831
Brian Osmancf860852018-10-31 14:04:39 -0400832 DashingCircleEffect(const SkPMColor4f&, AAMode aaMode, const SkMatrix& localMatrix,
joshualittb8c241a2015-05-19 08:23:30 -0700833 bool usesLocalCoords);
egdanielf767e792014-07-02 06:21:32 -0700834
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500835 SkPMColor4f fColor;
836 SkMatrix fLocalMatrix;
837 bool fUsesLocalCoords;
838 AAMode fAAMode;
Brian Salomon92be2f72018-06-19 14:33:47 -0400839
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500840 Attribute fInPosition;
841 Attribute fInDashParams;
842 Attribute fInCircleParams;
egdanielf767e792014-07-02 06:21:32 -0700843
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400844 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
egdanielf767e792014-07-02 06:21:32 -0700845
joshualitt249af152014-09-15 11:41:13 -0700846 typedef GrGeometryProcessor INHERITED;
egdanielf767e792014-07-02 06:21:32 -0700847};
848
849//////////////////////////////////////////////////////////////////////////////
850
egdaniele659a582015-11-13 09:55:43 -0800851class GLDashingCircleEffect : public GrGLSLGeometryProcessor {
egdanielf767e792014-07-02 06:21:32 -0700852public:
joshualitt465283c2015-09-11 08:19:35 -0700853 GLDashingCircleEffect();
egdanielf767e792014-07-02 06:21:32 -0700854
mtklein36352bf2015-03-25 18:17:31 -0700855 void onEmitCode(EmitArgs&, GrGPArgs*) override;
egdanielf767e792014-07-02 06:21:32 -0700856
joshualitt87f48d92014-12-04 10:41:40 -0800857 static inline void GenKey(const GrGeometryProcessor&,
Brian Salomon94efbf52016-11-29 13:43:05 -0500858 const GrShaderCaps&,
joshualitt87f48d92014-12-04 10:41:40 -0800859 GrProcessorKeyBuilder*);
egdanielf767e792014-07-02 06:21:32 -0700860
bsalomona624bf32016-09-20 09:12:47 -0700861 void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
Brian Salomonc241b582019-11-27 08:57:17 -0500862 const CoordTransformRange& transformRange) override;
863
egdanielf767e792014-07-02 06:21:32 -0700864private:
joshualitt9b989322014-12-15 14:16:27 -0800865 UniformHandle fParamUniform;
866 UniformHandle fColorUniform;
Brian Osmancf860852018-10-31 14:04:39 -0400867 SkPMColor4f fColor;
joshualitt9b989322014-12-15 14:16:27 -0800868 SkScalar fPrevRadius;
869 SkScalar fPrevCenterX;
870 SkScalar fPrevIntervalLength;
egdaniele659a582015-11-13 09:55:43 -0800871 typedef GrGLSLGeometryProcessor INHERITED;
egdanielf767e792014-07-02 06:21:32 -0700872};
873
joshualitt465283c2015-09-11 08:19:35 -0700874GLDashingCircleEffect::GLDashingCircleEffect() {
Brian Osmancf860852018-10-31 14:04:39 -0400875 fColor = SK_PMColor4fILLEGAL;
egdanielf767e792014-07-02 06:21:32 -0700876 fPrevRadius = SK_ScalarMin;
877 fPrevCenterX = SK_ScalarMin;
878 fPrevIntervalLength = SK_ScalarMax;
879}
880
robertphillips46d36f02015-01-18 08:14:14 -0800881void GLDashingCircleEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
joshualittc369e7c2014-10-22 10:56:26 -0700882 const DashingCircleEffect& dce = args.fGP.cast<DashingCircleEffect>();
egdaniel4ca2e602015-11-18 08:01:26 -0800883 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800884 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800885 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800886
joshualittabb52a12015-01-13 15:02:10 -0800887 // emit attributes
egdaniel0eafe792015-11-20 14:01:22 -0800888 varyingHandler->emitAttributes(dce);
joshualittabb52a12015-01-13 15:02:10 -0800889
joshualitt5224ba72015-02-03 15:07:51 -0800890 // XY are dashPos, Z is dashInterval
Chris Dalton27372882017-12-08 13:34:21 -0700891 GrGLSLVarying dashParams(kHalf3_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800892 varyingHandler->addVarying("DashParam", &dashParams);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500893 vertBuilder->codeAppendf("%s = %s;", dashParams.vsOut(), dce.fInDashParams.name());
joshualitt5224ba72015-02-03 15:07:51 -0800894
senorblancof3c2c462015-04-20 14:44:26 -0700895 // x refers to circle radius - 0.5, y refers to cicle's center x coord
Chris Dalton27372882017-12-08 13:34:21 -0700896 GrGLSLVarying circleParams(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800897 varyingHandler->addVarying("CircleParams", &circleParams);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500898 vertBuilder->codeAppendf("%s = %s;", circleParams.vsOut(), dce.fInCircleParams.name());
joshualitt30ba4362014-08-21 20:18:45 -0700899
Chris Dalton60283612018-02-14 13:38:14 -0700900 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt9b989322014-12-15 14:16:27 -0800901 // Setup pass through color
Brian Salomonbfd51832017-01-04 13:22:08 -0500902 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform);
joshualitt9b989322014-12-15 14:16:27 -0800903
joshualittabb52a12015-01-13 15:02:10 -0800904 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500905 this->writeOutputPosition(vertBuilder, gpArgs, dce.fInPosition.name());
joshualitt4973d9d2014-11-08 09:24:25 -0800906
joshualittabb52a12015-01-13 15:02:10 -0800907 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800908 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800909 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800910 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500911 dce.fInPosition.asShaderVar(),
egdaniel4ca2e602015-11-18 08:01:26 -0800912 dce.localMatrix(),
bsalomona624bf32016-09-20 09:12:47 -0700913 args.fFPCoordTransformHandler);
joshualittabb52a12015-01-13 15:02:10 -0800914
egdanielf767e792014-07-02 06:21:32 -0700915 // transforms all points so that we can compare them to our test circle
Ethan Nicholase1f55022019-02-05 17:17:40 -0500916 fragBuilder->codeAppendf("half xShifted = half(%s.x - floor(%s.x / %s.z) * %s.z);",
egdaniel4ca2e602015-11-18 08:01:26 -0800917 dashParams.fsIn(), dashParams.fsIn(), dashParams.fsIn(),
918 dashParams.fsIn());
Ethan Nicholase1f55022019-02-05 17:17:40 -0500919 fragBuilder->codeAppendf("half2 fragPosShifted = half2(xShifted, half(%s.y));",
920 dashParams.fsIn());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400921 fragBuilder->codeAppendf("half2 center = half2(%s.y, 0.0);", circleParams.fsIn());
922 fragBuilder->codeAppend("half dist = length(center - fragPosShifted);");
bsalomonaf18fb42016-06-07 08:10:46 -0700923 if (dce.aaMode() != AAMode::kNone) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400924 fragBuilder->codeAppendf("half diff = dist - %s.x;", circleParams.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800925 fragBuilder->codeAppend("diff = 1.0 - diff;");
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400926 fragBuilder->codeAppend("half alpha = saturate(diff);");
egdanielf767e792014-07-02 06:21:32 -0700927 } else {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400928 fragBuilder->codeAppendf("half alpha = 1.0;");
egdaniel4ca2e602015-11-18 08:01:26 -0800929 fragBuilder->codeAppendf("alpha *= dist < %s.x + 0.5 ? 1.0 : 0.0;", circleParams.fsIn());
egdanielf767e792014-07-02 06:21:32 -0700930 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400931 fragBuilder->codeAppendf("%s = half4(alpha);", args.fOutputCoverage);
egdanielf767e792014-07-02 06:21:32 -0700932}
933
egdaniel018fb622015-10-28 07:26:40 -0700934void GLDashingCircleEffect::setData(const GrGLSLProgramDataManager& pdman,
bsalomona624bf32016-09-20 09:12:47 -0700935 const GrPrimitiveProcessor& processor,
Brian Salomonc241b582019-11-27 08:57:17 -0500936 const CoordTransformRange& transformRange) {
joshualittb8c241a2015-05-19 08:23:30 -0700937 const DashingCircleEffect& dce = processor.cast<DashingCircleEffect>();
938 if (dce.color() != fColor) {
Brian Osmancf860852018-10-31 14:04:39 -0400939 pdman.set4fv(fColorUniform, 1, dce.color().vec());
joshualittb8c241a2015-05-19 08:23:30 -0700940 fColor = dce.color();
joshualitt9b989322014-12-15 14:16:27 -0800941 }
Brian Salomonc241b582019-11-27 08:57:17 -0500942 this->setTransformDataHelper(dce.localMatrix(), pdman, transformRange);
egdanielf767e792014-07-02 06:21:32 -0700943}
944
robertphillips46d36f02015-01-18 08:14:14 -0800945void GLDashingCircleEffect::GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500946 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700947 GrProcessorKeyBuilder* b) {
robertphillips46d36f02015-01-18 08:14:14 -0800948 const DashingCircleEffect& dce = gp.cast<DashingCircleEffect>();
949 uint32_t key = 0;
joshualittb8c241a2015-05-19 08:23:30 -0700950 key |= dce.usesLocalCoords() && dce.localMatrix().hasPerspective() ? 0x1 : 0x0;
Brian Salomonbfd51832017-01-04 13:22:08 -0500951 key |= static_cast<uint32_t>(dce.aaMode()) << 1;
joshualittb8c241a2015-05-19 08:23:30 -0700952 b->add32(key);
egdanielf767e792014-07-02 06:21:32 -0700953}
954
955//////////////////////////////////////////////////////////////////////////////
956
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500957GrGeometryProcessor* DashingCircleEffect::Make(SkArenaAlloc* arena,
958 const SkPMColor4f& color,
959 AAMode aaMode,
960 const SkMatrix& localMatrix,
961 bool usesLocalCoords) {
962 return arena->make<DashingCircleEffect>(color, aaMode, localMatrix, usesLocalCoords);
egdanielf767e792014-07-02 06:21:32 -0700963}
964
Brian Salomon94efbf52016-11-29 13:43:05 -0500965void DashingCircleEffect::getGLSLProcessorKey(const GrShaderCaps& caps,
egdaniel57d3b032015-11-13 11:57:27 -0800966 GrProcessorKeyBuilder* b) const {
joshualitt465283c2015-09-11 08:19:35 -0700967 GLDashingCircleEffect::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800968}
969
Brian Salomon94efbf52016-11-29 13:43:05 -0500970GrGLSLPrimitiveProcessor* DashingCircleEffect::createGLSLInstance(const GrShaderCaps&) const {
joshualitt465283c2015-09-11 08:19:35 -0700971 return new GLDashingCircleEffect();
egdanielf767e792014-07-02 06:21:32 -0700972}
973
Brian Osmancf860852018-10-31 14:04:39 -0400974DashingCircleEffect::DashingCircleEffect(const SkPMColor4f& color,
bsalomonaf18fb42016-06-07 08:10:46 -0700975 AAMode aaMode,
joshualittb8c241a2015-05-19 08:23:30 -0700976 const SkMatrix& localMatrix,
977 bool usesLocalCoords)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500978 : INHERITED(kDashingCircleEffect_ClassID)
979 , fColor(color)
980 , fLocalMatrix(localMatrix)
981 , fUsesLocalCoords(usesLocalCoords)
982 , fAAMode(aaMode) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500983 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
984 fInDashParams = {"inDashParams", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
985 fInCircleParams = {"inCircleParams", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
986 this->setVertexAttributes(&fInPosition, 3);
egdanielf767e792014-07-02 06:21:32 -0700987}
988
joshualittb0a8a372014-09-23 09:50:21 -0700989GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingCircleEffect);
egdanielf767e792014-07-02 06:21:32 -0700990
Hal Canary6f6961e2017-01-31 13:50:44 -0500991#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500992GrGeometryProcessor* DashingCircleEffect::TestCreate(GrProcessorTestData* d) {
Brian Salomona6aa5902016-12-16 09:32:00 -0500993 AAMode aaMode = static_cast<AAMode>(d->fRandom->nextULessThan(GrDashOp::kAAModeCnt));
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500994 return DashingCircleEffect::Make(d->allocator(),
995 SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
Brian Osman1be2b7c2018-10-29 16:07:15 -0400996 aaMode, GrTest::TestMatrix(d->fRandom),
997 d->fRandom->nextBool());
egdanielf767e792014-07-02 06:21:32 -0700998}
Hal Canary6f6961e2017-01-31 13:50:44 -0500999#endif
egdanielf767e792014-07-02 06:21:32 -07001000
1001//////////////////////////////////////////////////////////////////////////////
1002
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001003class GLDashingLineEffect;
1004
egdanielf767e792014-07-02 06:21:32 -07001005/*
1006 * This effect will draw a dashed line. The width of the dash is given by the strokeWidth and the
1007 * length and spacing by the DashInfo. Both of the previous two parameters are in device space.
Ethan Nicholas5af9ea32017-07-28 15:19:46 -04001008 * This effect also requires the setting of a float2 vertex attribute for the the four corners of the
egdanielf767e792014-07-02 06:21:32 -07001009 * bounding rect. This attribute is the "dash position" of each vertex. In other words it is the
1010 * vertex coords (in device space) if we transform the line to be horizontal, with the start of
1011 * line at the origin then shifted to the right by half the off interval. The line then goes in the
1012 * positive x direction.
1013 */
joshualitt249af152014-09-15 11:41:13 -07001014class DashingLineEffect : public GrGeometryProcessor {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001015public:
1016 typedef SkPathEffect::DashInfo DashInfo;
1017
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001018 static GrGeometryProcessor* Make(SkArenaAlloc* arena,
1019 const SkPMColor4f&,
1020 AAMode aaMode,
1021 const SkMatrix& localMatrix,
1022 bool usesLocalCoords);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001023
mtklein36352bf2015-03-25 18:17:31 -07001024 const char* name() const override { return "DashingEffect"; }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001025
bsalomonaf18fb42016-06-07 08:10:46 -07001026 AAMode aaMode() const { return fAAMode; }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001027
Brian Osmancf860852018-10-31 14:04:39 -04001028 const SkPMColor4f& color() const { return fColor; }
joshualitt88c23fc2015-05-13 14:18:07 -07001029
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001030 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualitte3ababe2015-05-15 07:56:07 -07001031
joshualittb8c241a2015-05-19 08:23:30 -07001032 bool usesLocalCoords() const { return fUsesLocalCoords; }
1033
Brian Salomon94efbf52016-11-29 13:43:05 -05001034 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001035
Brian Salomon94efbf52016-11-29 13:43:05 -05001036 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001037
1038private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001039 friend class GLDashingLineEffect;
1040 friend class ::SkArenaAlloc; // for access to ctor
1041
Brian Osmancf860852018-10-31 14:04:39 -04001042 DashingLineEffect(const SkPMColor4f&, AAMode aaMode, const SkMatrix& localMatrix,
joshualittb8c241a2015-05-19 08:23:30 -07001043 bool usesLocalCoords);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001044
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001045 SkPMColor4f fColor;
1046 SkMatrix fLocalMatrix;
1047 bool fUsesLocalCoords;
1048 AAMode fAAMode;
Brian Salomon92be2f72018-06-19 14:33:47 -04001049
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001050 Attribute fInPosition;
1051 Attribute fInDashParams;
1052 Attribute fInRect;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001053
Brian Salomon0c26a9d2017-07-06 10:09:38 -04001054 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001055
joshualitt249af152014-09-15 11:41:13 -07001056 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001057};
1058
1059//////////////////////////////////////////////////////////////////////////////
1060
egdaniele659a582015-11-13 09:55:43 -08001061class GLDashingLineEffect : public GrGLSLGeometryProcessor {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001062public:
joshualitt465283c2015-09-11 08:19:35 -07001063 GLDashingLineEffect();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001064
mtklein36352bf2015-03-25 18:17:31 -07001065 void onEmitCode(EmitArgs&, GrGPArgs*) override;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001066
joshualitt87f48d92014-12-04 10:41:40 -08001067 static inline void GenKey(const GrGeometryProcessor&,
Brian Salomon94efbf52016-11-29 13:43:05 -05001068 const GrShaderCaps&,
joshualitt87f48d92014-12-04 10:41:40 -08001069 GrProcessorKeyBuilder*);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001070
bsalomona624bf32016-09-20 09:12:47 -07001071 void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
Brian Salomonc241b582019-11-27 08:57:17 -05001072 const CoordTransformRange&) override;
joshualitte3ababe2015-05-15 07:56:07 -07001073
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001074private:
Brian Osmancf860852018-10-31 14:04:39 -04001075 SkPMColor4f fColor;
joshualitt9b989322014-12-15 14:16:27 -08001076 UniformHandle fColorUniform;
egdaniele659a582015-11-13 09:55:43 -08001077 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001078};
1079
Brian Osmancf860852018-10-31 14:04:39 -04001080GLDashingLineEffect::GLDashingLineEffect() : fColor(SK_PMColor4fILLEGAL) {}
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001081
robertphillips46d36f02015-01-18 08:14:14 -08001082void GLDashingLineEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
joshualittc369e7c2014-10-22 10:56:26 -07001083 const DashingLineEffect& de = args.fGP.cast<DashingLineEffect>();
joshualitt2dd1ae02014-12-03 06:24:10 -08001084
egdaniel4ca2e602015-11-18 08:01:26 -08001085 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -08001086 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -08001087 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -08001088
joshualittabb52a12015-01-13 15:02:10 -08001089 // emit attributes
egdaniel0eafe792015-11-20 14:01:22 -08001090 varyingHandler->emitAttributes(de);
joshualittabb52a12015-01-13 15:02:10 -08001091
joshualitt5224ba72015-02-03 15:07:51 -08001092 // XY refers to dashPos, Z is the dash interval length
Chris Dalton27372882017-12-08 13:34:21 -07001093 GrGLSLVarying inDashParams(kFloat3_GrSLType);
Chris Daltonfdde34e2017-10-16 14:15:26 -06001094 varyingHandler->addVarying("DashParams", &inDashParams);
Brian Osmanf04fb3c2018-11-12 15:34:00 -05001095 vertBuilder->codeAppendf("%s = %s;", inDashParams.vsOut(), de.fInDashParams.name());
joshualitt5224ba72015-02-03 15:07:51 -08001096
1097 // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5),
1098 // respectively.
Chris Dalton27372882017-12-08 13:34:21 -07001099 GrGLSLVarying inRectParams(kFloat4_GrSLType);
Chris Daltonfdde34e2017-10-16 14:15:26 -06001100 varyingHandler->addVarying("RectParams", &inRectParams);
Brian Osmanf04fb3c2018-11-12 15:34:00 -05001101 vertBuilder->codeAppendf("%s = %s;", inRectParams.vsOut(), de.fInRect.name());
joshualitt2dd1ae02014-12-03 06:24:10 -08001102
Chris Dalton60283612018-02-14 13:38:14 -07001103 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt9b989322014-12-15 14:16:27 -08001104 // Setup pass through color
Brian Salomonbfd51832017-01-04 13:22:08 -05001105 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform);
joshualittb8c241a2015-05-19 08:23:30 -07001106
joshualittabb52a12015-01-13 15:02:10 -08001107 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -05001108 this->writeOutputPosition(vertBuilder, gpArgs, de.fInPosition.name());
joshualitt4973d9d2014-11-08 09:24:25 -08001109
joshualittabb52a12015-01-13 15:02:10 -08001110 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -08001111 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -08001112 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -08001113 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -05001114 de.fInPosition.asShaderVar(),
egdaniel4ca2e602015-11-18 08:01:26 -08001115 de.localMatrix(),
bsalomona624bf32016-09-20 09:12:47 -07001116 args.fFPCoordTransformHandler);
joshualittabb52a12015-01-13 15:02:10 -08001117
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001118 // transforms all points so that we can compare them to our test rect
Ethan Nicholase1f55022019-02-05 17:17:40 -05001119 fragBuilder->codeAppendf("half xShifted = half(%s.x - floor(%s.x / %s.z) * %s.z);",
egdaniel4ca2e602015-11-18 08:01:26 -08001120 inDashParams.fsIn(), inDashParams.fsIn(), inDashParams.fsIn(),
1121 inDashParams.fsIn());
Ethan Nicholase1f55022019-02-05 17:17:40 -05001122 fragBuilder->codeAppendf("half2 fragPosShifted = half2(xShifted, half(%s.y));",
1123 inDashParams.fsIn());
bsalomonaf18fb42016-06-07 08:10:46 -07001124 if (de.aaMode() == AAMode::kCoverage) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001125 // The amount of coverage removed in x and y by the edges is computed as a pair of negative
1126 // numbers, xSub and ySub.
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001127 fragBuilder->codeAppend("half xSub, ySub;");
Ethan Nicholase1f55022019-02-05 17:17:40 -05001128 fragBuilder->codeAppendf("xSub = half(min(fragPosShifted.x - %s.x, 0.0));",
1129 inRectParams.fsIn());
1130 fragBuilder->codeAppendf("xSub += half(min(%s.z - fragPosShifted.x, 0.0));",
1131 inRectParams.fsIn());
1132 fragBuilder->codeAppendf("ySub = half(min(fragPosShifted.y - %s.y, 0.0));",
1133 inRectParams.fsIn());
1134 fragBuilder->codeAppendf("ySub += half(min(%s.w - fragPosShifted.y, 0.0));",
1135 inRectParams.fsIn());
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001136 // Now compute coverage in x and y and multiply them to get the fraction of the pixel
1137 // covered.
egdaniel4ca2e602015-11-18 08:01:26 -08001138 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001139 "half alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));");
bsalomonaf18fb42016-06-07 08:10:46 -07001140 } else if (de.aaMode() == AAMode::kCoverageWithMSAA) {
senorblancof3c2c462015-04-20 14:44:26 -07001141 // For MSAA, we don't modulate the alpha by the Y distance, since MSAA coverage will handle
1142 // AA on the the top and bottom edges. The shader is only responsible for intra-dash alpha.
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001143 fragBuilder->codeAppend("half xSub;");
Ethan Nicholase1f55022019-02-05 17:17:40 -05001144 fragBuilder->codeAppendf("xSub = half(min(fragPosShifted.x - %s.x, 0.0));",
1145 inRectParams.fsIn());
1146 fragBuilder->codeAppendf("xSub += half(min(%s.z - fragPosShifted.x, 0.0));",
1147 inRectParams.fsIn());
senorblancof3c2c462015-04-20 14:44:26 -07001148 // Now compute coverage in x to get the fraction of the pixel covered.
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001149 fragBuilder->codeAppendf("half alpha = (1.0 + max(xSub, -1.0));");
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001150 } else {
1151 // Assuming the bounding geometry is tight so no need to check y values
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001152 fragBuilder->codeAppendf("half alpha = 1.0;");
egdaniel4ca2e602015-11-18 08:01:26 -08001153 fragBuilder->codeAppendf("alpha *= (fragPosShifted.x - %s.x) > -0.5 ? 1.0 : 0.0;",
1154 inRectParams.fsIn());
1155 fragBuilder->codeAppendf("alpha *= (%s.z - fragPosShifted.x) >= -0.5 ? 1.0 : 0.0;",
1156 inRectParams.fsIn());
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001157 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -04001158 fragBuilder->codeAppendf("%s = half4(alpha);", args.fOutputCoverage);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001159}
1160
egdaniel018fb622015-10-28 07:26:40 -07001161void GLDashingLineEffect::setData(const GrGLSLProgramDataManager& pdman,
bsalomona624bf32016-09-20 09:12:47 -07001162 const GrPrimitiveProcessor& processor,
Brian Salomonc241b582019-11-27 08:57:17 -05001163 const CoordTransformRange& transformRange) {
joshualittb8c241a2015-05-19 08:23:30 -07001164 const DashingLineEffect& de = processor.cast<DashingLineEffect>();
1165 if (de.color() != fColor) {
Brian Osmancf860852018-10-31 14:04:39 -04001166 pdman.set4fv(fColorUniform, 1, de.color().vec());
joshualittb8c241a2015-05-19 08:23:30 -07001167 fColor = de.color();
joshualitt9b989322014-12-15 14:16:27 -08001168 }
Brian Salomonc241b582019-11-27 08:57:17 -05001169 this->setTransformDataHelper(de.localMatrix(), pdman, transformRange);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001170}
1171
robertphillips46d36f02015-01-18 08:14:14 -08001172void GLDashingLineEffect::GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -05001173 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -07001174 GrProcessorKeyBuilder* b) {
robertphillips46d36f02015-01-18 08:14:14 -08001175 const DashingLineEffect& de = gp.cast<DashingLineEffect>();
1176 uint32_t key = 0;
joshualittb8c241a2015-05-19 08:23:30 -07001177 key |= de.usesLocalCoords() && de.localMatrix().hasPerspective() ? 0x1 : 0x0;
bsalomonaf18fb42016-06-07 08:10:46 -07001178 key |= static_cast<int>(de.aaMode()) << 8;
joshualittb8c241a2015-05-19 08:23:30 -07001179 b->add32(key);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001180}
1181
1182//////////////////////////////////////////////////////////////////////////////
skia.committer@gmail.com3b9e8be2014-05-20 03:05:34 +00001183
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001184GrGeometryProcessor* DashingLineEffect::Make(SkArenaAlloc* arena,
1185 const SkPMColor4f& color,
1186 AAMode aaMode,
1187 const SkMatrix& localMatrix,
1188 bool usesLocalCoords) {
1189 return arena->make<DashingLineEffect>(color, aaMode, localMatrix, usesLocalCoords);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001190}
1191
Brian Salomon94efbf52016-11-29 13:43:05 -05001192void DashingLineEffect::getGLSLProcessorKey(const GrShaderCaps& caps,
egdaniel57d3b032015-11-13 11:57:27 -08001193 GrProcessorKeyBuilder* b) const {
joshualitt465283c2015-09-11 08:19:35 -07001194 GLDashingLineEffect::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -08001195}
1196
Brian Salomon94efbf52016-11-29 13:43:05 -05001197GrGLSLPrimitiveProcessor* DashingLineEffect::createGLSLInstance(const GrShaderCaps&) const {
joshualitt465283c2015-09-11 08:19:35 -07001198 return new GLDashingLineEffect();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001199}
1200
Brian Osmancf860852018-10-31 14:04:39 -04001201DashingLineEffect::DashingLineEffect(const SkPMColor4f& color,
bsalomonaf18fb42016-06-07 08:10:46 -07001202 AAMode aaMode,
joshualittb8c241a2015-05-19 08:23:30 -07001203 const SkMatrix& localMatrix,
1204 bool usesLocalCoords)
Ethan Nicholasabff9562017-10-09 10:54:08 -04001205 : INHERITED(kDashingLineEffect_ClassID)
1206 , fColor(color)
joshualitte3ababe2015-05-15 07:56:07 -07001207 , fLocalMatrix(localMatrix)
joshualittb8c241a2015-05-19 08:23:30 -07001208 , fUsesLocalCoords(usesLocalCoords)
joshualitt88c23fc2015-05-13 14:18:07 -07001209 , fAAMode(aaMode) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -05001210 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1211 fInDashParams = {"inDashParams", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
1212 fInRect = {"inRect", kFloat4_GrVertexAttribType, kHalf4_GrSLType};
1213 this->setVertexAttributes(&fInPosition, 3);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001214}
1215
joshualittb0a8a372014-09-23 09:50:21 -07001216GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingLineEffect);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001217
Hal Canary6f6961e2017-01-31 13:50:44 -05001218#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001219GrGeometryProcessor* DashingLineEffect::TestCreate(GrProcessorTestData* d) {
Brian Salomona6aa5902016-12-16 09:32:00 -05001220 AAMode aaMode = static_cast<AAMode>(d->fRandom->nextULessThan(GrDashOp::kAAModeCnt));
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001221 return DashingLineEffect::Make(d->allocator(),
1222 SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
bungeman06ca8ec2016-06-09 08:01:03 -07001223 aaMode, GrTest::TestMatrix(d->fRandom),
1224 d->fRandom->nextBool());
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001225}
Mike Klein5045e502018-06-19 01:40:57 +00001226
Brian Salomon92be2f72018-06-19 14:33:47 -04001227#endif
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001228//////////////////////////////////////////////////////////////////////////////
1229
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001230static GrGeometryProcessor* make_dash_gp(SkArenaAlloc* arena,
1231 const SkPMColor4f& color,
1232 AAMode aaMode,
1233 DashCap cap,
1234 const SkMatrix& viewMatrix,
1235 bool usesLocalCoords) {
joshualittdf0c5572015-08-03 11:35:28 -07001236 SkMatrix invert;
1237 if (usesLocalCoords && !viewMatrix.invert(&invert)) {
1238 SkDebugf("Failed to invert\n");
halcanary96fcdcc2015-08-27 07:41:13 -07001239 return nullptr;
joshualittdf0c5572015-08-03 11:35:28 -07001240 }
1241
egdanielf767e792014-07-02 06:21:32 -07001242 switch (cap) {
joshualitt5224ba72015-02-03 15:07:51 -08001243 case kRound_DashCap:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001244 return DashingCircleEffect::Make(arena, color, aaMode, invert, usesLocalCoords);
joshualitt5224ba72015-02-03 15:07:51 -08001245 case kNonRound_DashCap:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001246 return DashingLineEffect::Make(arena, color, aaMode, invert, usesLocalCoords);
egdanielf767e792014-07-02 06:21:32 -07001247 }
halcanary96fcdcc2015-08-27 07:41:13 -07001248 return nullptr;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001249}
joshualittfa2008f2015-04-29 11:32:05 -07001250
1251/////////////////////////////////////////////////////////////////////////////////////////////////
1252
Hal Canary6f6961e2017-01-31 13:50:44 -05001253#if GR_TEST_UTILS
joshualittfa2008f2015-04-29 11:32:05 -07001254
Brian Salomon98222ac2017-07-12 15:27:54 -04001255GR_DRAW_OP_TEST_DEFINE(DashOp) {
joshualittfa2008f2015-04-29 11:32:05 -07001256 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
Brian Salomon98222ac2017-07-12 15:27:54 -04001257 AAMode aaMode;
1258 do {
1259 aaMode = static_cast<AAMode>(random->nextULessThan(GrDashOp::kAAModeCnt));
Chris Dalton6ce447a2019-06-23 18:07:38 -06001260 } while (AAMode::kCoverageWithMSAA == aaMode && numSamples <= 1);
joshualittfa2008f2015-04-29 11:32:05 -07001261
1262 // We can only dash either horizontal or vertical lines
1263 SkPoint pts[2];
1264 if (random->nextBool()) {
1265 // vertical
1266 pts[0].fX = 1.f;
1267 pts[0].fY = random->nextF() * 10.f;
1268 pts[1].fX = 1.f;
1269 pts[1].fY = random->nextF() * 10.f;
1270 } else {
1271 // horizontal
1272 pts[0].fX = random->nextF() * 10.f;
1273 pts[0].fY = 1.f;
1274 pts[1].fX = random->nextF() * 10.f;
1275 pts[1].fY = 1.f;
1276 }
1277
1278 // pick random cap
bsalomona7d85ba2016-07-06 11:54:59 -07001279 SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::kCapCount));
joshualittfa2008f2015-04-29 11:32:05 -07001280
1281 SkScalar intervals[2];
1282
1283 // We can only dash with the following intervals
1284 enum Intervals {
1285 kOpenOpen_Intervals ,
1286 kOpenClose_Intervals,
1287 kCloseOpen_Intervals,
1288 };
1289
Greg Daniel5d00f002017-06-29 13:44:51 -04001290 Intervals intervalType = SkPaint::kRound_Cap == cap ?
joshualittfa2008f2015-04-29 11:32:05 -07001291 kOpenClose_Intervals :
1292 Intervals(random->nextULessThan(kCloseOpen_Intervals + 1));
1293 static const SkScalar kIntervalMin = 0.1f;
Greg Daniel5d00f002017-06-29 13:44:51 -04001294 static const SkScalar kIntervalMinCircles = 1.f; // Must be >= to stroke width
joshualittfa2008f2015-04-29 11:32:05 -07001295 static const SkScalar kIntervalMax = 10.f;
1296 switch (intervalType) {
1297 case kOpenOpen_Intervals:
1298 intervals[0] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
1299 intervals[1] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
1300 break;
Greg Daniel5d00f002017-06-29 13:44:51 -04001301 case kOpenClose_Intervals: {
joshualittfa2008f2015-04-29 11:32:05 -07001302 intervals[0] = 0.f;
Greg Daniel5d00f002017-06-29 13:44:51 -04001303 SkScalar min = SkPaint::kRound_Cap == cap ? kIntervalMinCircles : kIntervalMin;
1304 intervals[1] = random->nextRangeScalar(min, kIntervalMax);
joshualittfa2008f2015-04-29 11:32:05 -07001305 break;
Greg Daniel5d00f002017-06-29 13:44:51 -04001306 }
joshualittfa2008f2015-04-29 11:32:05 -07001307 case kCloseOpen_Intervals:
1308 intervals[0] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
1309 intervals[1] = 0.f;
1310 break;
1311
1312 }
1313
1314 // phase is 0 < sum (i0, i1)
1315 SkScalar phase = random->nextRangeScalar(0, intervals[0] + intervals[1]);
1316
1317 SkPaint p;
1318 p.setStyle(SkPaint::kStroke_Style);
1319 p.setStrokeWidth(SkIntToScalar(1));
1320 p.setStrokeCap(cap);
bsalomon6663acf2016-05-10 09:14:17 -07001321 p.setPathEffect(GrTest::TestDashPathEffect::Make(intervals, 2, phase));
joshualittfa2008f2015-04-29 11:32:05 -07001322
bsalomon6663acf2016-05-10 09:14:17 -07001323 GrStyle style(p);
joshualittfa2008f2015-04-29 11:32:05 -07001324
Robert Phillips7c525e62018-06-12 10:11:12 -04001325 return GrDashOp::MakeDashLineOp(context, std::move(paint), viewMatrix, pts, aaMode, style,
Brian Salomon98222ac2017-07-12 15:27:54 -04001326 GrGetRandomStencil(random, context));
joshualittfa2008f2015-04-29 11:32:05 -07001327}
1328
1329#endif