blob: f1e8016eb9c81890f2dec4ea19e06a25301c3ae1 [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
8#include "GrDashingEffect.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualittfa2008f2015-04-29 11:32:05 -070011#include "GrBatchTest.h"
bsalomoneb1cb5c2015-05-22 08:01:09 -070012#include "GrCaps.h"
joshualittb0a8a372014-09-23 09:50:21 -070013#include "GrGeometryProcessor.h"
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000014#include "GrContext.h"
15#include "GrCoordTransform.h"
joshualitt5478d422014-11-14 16:00:38 -080016#include "GrDefaultGeoProcFactory.h"
egdaniel605dd0f2014-11-12 08:35:25 -080017#include "GrInvariantOutput.h"
joshualittb0a8a372014-09-23 09:50:21 -070018#include "GrProcessor.h"
egdaniele61c4112014-06-12 10:24:21 -070019#include "GrStrokeInfo.h"
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000020#include "SkGr.h"
bsalomon16b99132015-08-13 14:55:50 -070021#include "batches/GrVertexBatch.h"
egdaniel2d721d32015-11-11 13:06:05 -080022#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080023#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070024#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080025#include "glsl/GrGLSLUniformHandler.h"
egdaniel0eafe792015-11-20 14:01:22 -080026#include "glsl/GrGLSLVarying.h"
egdaniel2d721d32015-11-11 13:06:05 -080027#include "glsl/GrGLSLVertexShaderBuilder.h"
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000028
29///////////////////////////////////////////////////////////////////////////////
30
egdaniele61c4112014-06-12 10:24:21 -070031// Returns whether or not the gpu can fast path the dash line effect.
kkinnunen18996512015-04-26 23:18:49 -070032bool GrDashingEffect::CanDrawDashLine(const SkPoint pts[2], const GrStrokeInfo& strokeInfo,
33 const SkMatrix& viewMatrix) {
egdaniele61c4112014-06-12 10:24:21 -070034 // Pts must be either horizontal or vertical in src space
35 if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) {
36 return false;
37 }
38
39 // May be able to relax this to include skew. As of now cannot do perspective
40 // because of the non uniform scaling of bloating a rect
41 if (!viewMatrix.preservesRightAngles()) {
42 return false;
43 }
44
kkinnunen261694c2015-05-05 08:00:10 -070045 if (!strokeInfo.isDashed() || 2 != strokeInfo.getDashCount()) {
egdaniele61c4112014-06-12 10:24:21 -070046 return false;
47 }
48
kkinnunen261694c2015-05-05 08:00:10 -070049 const SkScalar* intervals = strokeInfo.getDashIntervals();
50 if (0 == intervals[0] && 0 == intervals[1]) {
egdaniele61c4112014-06-12 10:24:21 -070051 return false;
52 }
53
kkinnunend156d362015-05-18 22:23:54 -070054 SkPaint::Cap cap = strokeInfo.getCap();
egdaniele61c4112014-06-12 10:24:21 -070055 // Current we do don't handle Round or Square cap dashes
kkinnunen261694c2015-05-05 08:00:10 -070056 if (SkPaint::kRound_Cap == cap && intervals[0] != 0.f) {
egdaniele61c4112014-06-12 10:24:21 -070057 return false;
58 }
59
60 return true;
61}
62
63namespace {
egdaniele61c4112014-06-12 10:24:21 -070064struct DashLineVertex {
65 SkPoint fPos;
66 SkPoint fDashPos;
joshualitt5224ba72015-02-03 15:07:51 -080067 SkScalar fIntervalLength;
68 SkRect fRect;
69};
70struct DashCircleVertex {
71 SkPoint fPos;
72 SkPoint fDashPos;
73 SkScalar fIntervalLength;
74 SkScalar fRadius;
75 SkScalar fCenterX;
egdaniele61c4112014-06-12 10:24:21 -070076};
senorblancof3c2c462015-04-20 14:44:26 -070077
78enum DashAAMode {
79 kBW_DashAAMode,
80 kEdgeAA_DashAAMode,
81 kMSAA_DashAAMode,
82
83 kDashAAModeCount,
84};
egdaniele61c4112014-06-12 10:24:21 -070085};
86
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000087static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale,
88 const SkMatrix& viewMatrix, const SkPoint pts[2]) {
89 SkVector vecSrc = pts[1] - pts[0];
90 SkScalar magSrc = vecSrc.length();
91 SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0;
92 vecSrc.scale(invSrc);
93
94 SkVector vecSrcPerp;
95 vecSrc.rotateCW(&vecSrcPerp);
96 viewMatrix.mapVectors(&vecSrc, 1);
97 viewMatrix.mapVectors(&vecSrcPerp, 1);
98
99 // parallelScale tells how much to scale along the line parallel to the dash line
100 // perpScale tells how much to scale in the direction perpendicular to the dash line
101 *parallelScale = vecSrc.length();
102 *perpScale = vecSrcPerp.length();
103}
104
105// calculates the rotation needed to aligned pts to the x axis with pts[0] < pts[1]
106// Stores the rotation matrix in rotMatrix, and the mapped points in ptsRot
halcanary96fcdcc2015-08-27 07:41:13 -0700107static void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint ptsRot[2] = nullptr) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000108 SkVector vec = pts[1] - pts[0];
109 SkScalar mag = vec.length();
110 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
111
112 vec.scale(inv);
113 rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
114 if (ptsRot) {
115 rotMatrix->mapPoints(ptsRot, pts, 2);
116 // correction for numerical issues if map doesn't make ptsRot exactly horizontal
117 ptsRot[1].fY = pts[0].fY;
118 }
119}
120
121// Assumes phase < sum of all intervals
joshualitt4f569be2015-02-27 11:41:49 -0800122static SkScalar calc_start_adjustment(const SkScalar intervals[2], SkScalar phase) {
123 SkASSERT(phase < intervals[0] + intervals[1]);
124 if (phase >= intervals[0] && phase != 0) {
125 SkScalar srcIntervalLen = intervals[0] + intervals[1];
126 return srcIntervalLen - phase;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000127 }
128 return 0;
129}
130
joshualitt4f569be2015-02-27 11:41:49 -0800131static SkScalar calc_end_adjustment(const SkScalar intervals[2], const SkPoint pts[2],
egdaniele61c4112014-06-12 10:24:21 -0700132 SkScalar phase, SkScalar* endingInt) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000133 if (pts[1].fX <= pts[0].fX) {
134 return 0;
135 }
joshualitt4f569be2015-02-27 11:41:49 -0800136 SkScalar srcIntervalLen = intervals[0] + intervals[1];
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000137 SkScalar totalLen = pts[1].fX - pts[0].fX;
reed80ea19c2015-05-12 10:37:34 -0700138 SkScalar temp = totalLen / srcIntervalLen;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000139 SkScalar numFullIntervals = SkScalarFloorToScalar(temp);
egdaniele61c4112014-06-12 10:24:21 -0700140 *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase;
reed80ea19c2015-05-12 10:37:34 -0700141 temp = *endingInt / srcIntervalLen;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000142 *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen;
143 if (0 == *endingInt) {
144 *endingInt = srcIntervalLen;
145 }
joshualitt4f569be2015-02-27 11:41:49 -0800146 if (*endingInt > intervals[0]) {
147 if (0 == intervals[0]) {
commit-bot@chromium.orgad883402014-05-19 14:43:45 +0000148 *endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps)
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000149 }
joshualitt4f569be2015-02-27 11:41:49 -0800150 return *endingInt - intervals[0];
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000151 }
152 return 0;
153}
154
joshualitt5224ba72015-02-03 15:07:51 -0800155enum DashCap {
156 kRound_DashCap,
157 kNonRound_DashCap,
158};
159
160static int kDashVertices = 4;
161
162template <typename T>
163void setup_dashed_rect_common(const SkRect& rect, const SkMatrix& matrix, T* vertices, int idx,
senorblancof3c2c462015-04-20 14:44:26 -0700164 SkScalar offset, SkScalar bloatX, SkScalar bloatY, SkScalar len,
165 SkScalar stroke) {
166 SkScalar startDashX = offset - bloatX;
167 SkScalar endDashX = offset + len + bloatX;
168 SkScalar startDashY = -stroke - bloatY;
169 SkScalar endDashY = stroke + bloatY;
joshualitt5224ba72015-02-03 15:07:51 -0800170 vertices[idx].fDashPos = SkPoint::Make(startDashX , startDashY);
171 vertices[idx + 1].fDashPos = SkPoint::Make(startDashX, endDashY);
172 vertices[idx + 2].fDashPos = SkPoint::Make(endDashX, endDashY);
173 vertices[idx + 3].fDashPos = SkPoint::Make(endDashX, startDashY);
174
175 vertices[idx].fPos = SkPoint::Make(rect.fLeft, rect.fTop);
176 vertices[idx + 1].fPos = SkPoint::Make(rect.fLeft, rect.fBottom);
177 vertices[idx + 2].fPos = SkPoint::Make(rect.fRight, rect.fBottom);
178 vertices[idx + 3].fPos = SkPoint::Make(rect.fRight, rect.fTop);
179
180 matrix.mapPointsWithStride(&vertices[idx].fPos, sizeof(T), 4);
181}
182
183static void setup_dashed_rect(const SkRect& rect, void* vertices, int idx,
senorblancof3c2c462015-04-20 14:44:26 -0700184 const SkMatrix& matrix, SkScalar offset, SkScalar bloatX,
185 SkScalar bloatY, SkScalar len, SkScalar stroke,
186 SkScalar startInterval, SkScalar endInterval, SkScalar strokeWidth,
187 DashCap cap, const size_t vertexStride) {
joshualitt5224ba72015-02-03 15:07:51 -0800188 SkScalar intervalLength = startInterval + endInterval;
189
190 if (kRound_DashCap == cap) {
191 SkASSERT(vertexStride == sizeof(DashCircleVertex));
192 DashCircleVertex* verts = reinterpret_cast<DashCircleVertex*>(vertices);
193
senorblancof3c2c462015-04-20 14:44:26 -0700194 setup_dashed_rect_common<DashCircleVertex>(rect, matrix, verts, idx, offset, bloatX,
195 bloatY, len, stroke);
joshualitt5224ba72015-02-03 15:07:51 -0800196
197 SkScalar radius = SkScalarHalf(strokeWidth) - 0.5f;
198 SkScalar centerX = SkScalarHalf(endInterval);
199
200 for (int i = 0; i < kDashVertices; i++) {
201 verts[idx + i].fIntervalLength = intervalLength;
202 verts[idx + i].fRadius = radius;
203 verts[idx + i].fCenterX = centerX;
204 }
205
206 } else {
207 SkASSERT(kNonRound_DashCap == cap && vertexStride == sizeof(DashLineVertex));
208 DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(vertices);
209
senorblancof3c2c462015-04-20 14:44:26 -0700210 setup_dashed_rect_common<DashLineVertex>(rect, matrix, verts, idx, offset, bloatX,
211 bloatY, len, stroke);
joshualitt5224ba72015-02-03 15:07:51 -0800212
213 SkScalar halfOffLen = SkScalarHalf(endInterval);
214 SkScalar halfStroke = SkScalarHalf(strokeWidth);
215 SkRect rectParam;
216 rectParam.set(halfOffLen + 0.5f, -halfStroke + 0.5f,
217 halfOffLen + startInterval - 0.5f, halfStroke - 0.5f);
218 for (int i = 0; i < kDashVertices; i++) {
219 verts[idx + i].fIntervalLength = intervalLength;
220 verts[idx + i].fRect = rectParam;
221 }
222 }
egdaniele61c4112014-06-12 10:24:21 -0700223}
224
joshualitt5478d422014-11-14 16:00:38 -0800225static void setup_dashed_rect_pos(const SkRect& rect, int idx, const SkMatrix& matrix,
226 SkPoint* verts) {
227 verts[idx] = SkPoint::Make(rect.fLeft, rect.fTop);
228 verts[idx + 1] = SkPoint::Make(rect.fLeft, rect.fBottom);
229 verts[idx + 2] = SkPoint::Make(rect.fRight, rect.fBottom);
230 verts[idx + 3] = SkPoint::Make(rect.fRight, rect.fTop);
231 matrix.mapPoints(&verts[idx], 4);
232}
egdaniele61c4112014-06-12 10:24:21 -0700233
joshualitt5224ba72015-02-03 15:07:51 -0800234
235/**
236 * An GrGeometryProcessor that renders a dashed line.
237 * This GrGeometryProcessor is meant for dashed lines that only have a single on/off interval pair.
238 * Bounding geometry is rendered and the effect computes coverage based on the fragment's
239 * position relative to the dashed line.
240 */
241static GrGeometryProcessor* create_dash_gp(GrColor,
senorblancof3c2c462015-04-20 14:44:26 -0700242 DashAAMode aaMode,
joshualitt5224ba72015-02-03 15:07:51 -0800243 DashCap cap,
joshualittb8c241a2015-05-19 08:23:30 -0700244 const SkMatrix& localMatrix,
245 bool usesLocalCoords);
joshualitt5224ba72015-02-03 15:07:51 -0800246
bsalomonabd30f52015-08-13 13:34:48 -0700247class DashBatch : public GrVertexBatch {
joshualitt4f569be2015-02-27 11:41:49 -0800248public:
reed1b55a962015-09-17 20:16:13 -0700249 DEFINE_BATCH_CLASS_ID
250
joshualitt4f569be2015-02-27 11:41:49 -0800251 struct Geometry {
joshualitt4f569be2015-02-27 11:41:49 -0800252 SkMatrix fViewMatrix;
253 SkMatrix fSrcRotInv;
254 SkPoint fPtsRot[2];
255 SkScalar fSrcStrokeWidth;
256 SkScalar fPhase;
257 SkScalar fIntervals[2];
258 SkScalar fParallelScale;
259 SkScalar fPerpendicularScale;
reed1b55a962015-09-17 20:16:13 -0700260 GrColor fColor;
joshualitt4f569be2015-02-27 11:41:49 -0800261 };
262
bsalomonabd30f52015-08-13 13:34:48 -0700263 static GrDrawBatch* Create(const Geometry& geometry, SkPaint::Cap cap, DashAAMode aaMode,
264 bool fullDash) {
halcanary385fe4d2015-08-26 13:07:48 -0700265 return new DashBatch(geometry, cap, aaMode, fullDash);
joshualitt4f569be2015-02-27 11:41:49 -0800266 }
267
mtklein36352bf2015-03-25 18:17:31 -0700268 const char* name() const override { return "DashBatch"; }
joshualitt4f569be2015-02-27 11:41:49 -0800269
halcanary9d524f22016-03-29 09:03:52 -0700270 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800271 GrInitInvariantOutput* coverage,
272 GrBatchToXPOverrides* overrides) const override {
joshualitt4f569be2015-02-27 11:41:49 -0800273 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800274 color->setKnownFourComponents(fGeoData[0].fColor);
275 coverage->setUnknownSingleComponent();
joshualitt4f569be2015-02-27 11:41:49 -0800276 }
277
bsalomone46f9fe2015-08-18 06:05:14 -0700278 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
279
280private:
reed1b55a962015-09-17 20:16:13 -0700281 DashBatch(const Geometry& geometry, SkPaint::Cap cap, DashAAMode aaMode, bool fullDash)
282 : INHERITED(ClassID()) {
bsalomone46f9fe2015-08-18 06:05:14 -0700283 fGeoData.push_back(geometry);
284
285 fBatch.fAAMode = aaMode;
286 fBatch.fCap = cap;
287 fBatch.fFullDash = fullDash;
288
289 // compute bounds
290 SkScalar halfStrokeWidth = 0.5f * geometry.fSrcStrokeWidth;
291 SkScalar xBloat = SkPaint::kButt_Cap == cap ? 0 : halfStrokeWidth;
292 fBounds.set(geometry.fPtsRot[0], geometry.fPtsRot[1]);
293 fBounds.outset(xBloat, halfStrokeWidth);
294
295 // Note, we actually create the combined matrix here, and save the work
296 SkMatrix& combinedMatrix = fGeoData[0].fSrcRotInv;
297 combinedMatrix.postConcat(geometry.fViewMatrix);
298 combinedMatrix.mapRect(&fBounds);
299 }
300
ethannicholasff210322015-11-24 12:10:10 -0800301 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
joshualitt4f569be2015-02-27 11:41:49 -0800302 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800303 if (!overrides.readsColor()) {
joshualitt4f569be2015-02-27 11:41:49 -0800304 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt4f569be2015-02-27 11:41:49 -0800305 }
ethannicholasff210322015-11-24 12:10:10 -0800306 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt4f569be2015-02-27 11:41:49 -0800307
308 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800309 fBatch.fColorIgnored = !overrides.readsColor();
joshualitt4f569be2015-02-27 11:41:49 -0800310 fBatch.fColor = fGeoData[0].fColor;
ethannicholasff210322015-11-24 12:10:10 -0800311 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
312 fBatch.fCoverageIgnored = !overrides.readsCoverage();
joshualitt4f569be2015-02-27 11:41:49 -0800313 }
314
315 struct DashDraw {
joshualitt144c3c82015-11-30 12:30:13 -0800316 DashDraw(const Geometry& geo) {
317 memcpy(fPtsRot, geo.fPtsRot, sizeof(geo.fPtsRot));
318 memcpy(fIntervals, geo.fIntervals, sizeof(geo.fIntervals));
319 fPhase = geo.fPhase;
320 }
321 SkPoint fPtsRot[2];
322 SkScalar fIntervals[2];
323 SkScalar fPhase;
joshualitt4f569be2015-02-27 11:41:49 -0800324 SkScalar fStartOffset;
325 SkScalar fStrokeWidth;
326 SkScalar fLineLength;
327 SkScalar fHalfDevStroke;
senorblancof3c2c462015-04-20 14:44:26 -0700328 SkScalar fDevBloatX;
329 SkScalar fDevBloatY;
joshualitt4f569be2015-02-27 11:41:49 -0800330 bool fLineDone;
331 bool fHasStartRect;
332 bool fHasEndRect;
333 };
334
joshualitt144c3c82015-11-30 12:30:13 -0800335 void onPrepareDraws(Target* target) const override {
joshualitt4f569be2015-02-27 11:41:49 -0800336 int instanceCount = fGeoData.count();
joshualitt4f569be2015-02-27 11:41:49 -0800337 SkPaint::Cap cap = this->cap();
joshualitte494a582015-08-03 09:32:36 -0700338 bool isRoundCap = SkPaint::kRound_Cap == cap;
339 DashCap capType = isRoundCap ? kRound_DashCap : kNonRound_DashCap;
joshualittdf0c5572015-08-03 11:35:28 -0700340
341 SkAutoTUnref<const GrGeometryProcessor> gp;
joshualitt4f569be2015-02-27 11:41:49 -0800342 if (this->fullDash()) {
joshualittdf0c5572015-08-03 11:35:28 -0700343 gp.reset(create_dash_gp(this->color(), this->aaMode(), capType, this->viewMatrix(),
joshualittb8c241a2015-05-19 08:23:30 -0700344 this->usesLocalCoords()));
joshualitt4f569be2015-02-27 11:41:49 -0800345 } else {
346 // Set up the vertex data for the line and start/end dashes
joshualittdf0c5572015-08-03 11:35:28 -0700347 using namespace GrDefaultGeoProcFactory;
348 Color color(this->color());
349 Coverage coverage(this->coverageIgnored() ? Coverage::kNone_Type :
350 Coverage::kSolid_Type);
351 LocalCoords localCoords(this->usesLocalCoords() ? LocalCoords::kUsePosition_Type :
352 LocalCoords::kUnused_Type);
353 gp.reset(CreateForDeviceSpace(color, coverage, localCoords, this->viewMatrix()));
354 }
355
356 if (!gp) {
357 SkDebugf("Could not create GrGeometryProcessor\n");
358 return;
joshualitt4f569be2015-02-27 11:41:49 -0800359 }
360
senorblancof3c2c462015-04-20 14:44:26 -0700361 // useAA here means Edge AA or MSAA
362 bool useAA = this->aaMode() != kBW_DashAAMode;
joshualitt4f569be2015-02-27 11:41:49 -0800363 bool fullDash = this->fullDash();
364
365 // We do two passes over all of the dashes. First we setup the start, end, and bounds,
366 // rectangles. We preserve all of this work in the rects / draws arrays below. Then we
367 // iterate again over these decomposed dashes to generate vertices
joshualitt144c3c82015-11-30 12:30:13 -0800368 static const int kNumStackDashes = 128;
369 SkSTArray<kNumStackDashes, SkRect, true> rects;
370 SkSTArray<kNumStackDashes, DashDraw, true> draws;
joshualitt4f569be2015-02-27 11:41:49 -0800371
372 int totalRectCount = 0;
joshualittd0f54572015-03-02 12:00:52 -0800373 int rectOffset = 0;
bsalomonb5238a72015-05-05 07:49:49 -0700374 rects.push_back_n(3 * instanceCount);
joshualitt4f569be2015-02-27 11:41:49 -0800375 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800376 const Geometry& args = fGeoData[i];
377
378 DashDraw& draw = draws.push_back(args);
joshualitt4f569be2015-02-27 11:41:49 -0800379
380 bool hasCap = SkPaint::kButt_Cap != cap && 0 != args.fSrcStrokeWidth;
381
382 // We always want to at least stroke out half a pixel on each side in device space
383 // so 0.5f / perpScale gives us this min in src space
384 SkScalar halfSrcStroke =
385 SkMaxScalar(args.fSrcStrokeWidth * 0.5f, 0.5f / args.fPerpendicularScale);
386
387 SkScalar strokeAdj;
388 if (!hasCap) {
389 strokeAdj = 0.f;
390 } else {
391 strokeAdj = halfSrcStroke;
392 }
393
394 SkScalar startAdj = 0;
395
joshualitt4f569be2015-02-27 11:41:49 -0800396 bool lineDone = false;
397
398 // Too simplify the algorithm, we always push back rects for start and end rect.
399 // Otherwise we'd have to track start / end rects for each individual geometry
joshualittd0f54572015-03-02 12:00:52 -0800400 SkRect& bounds = rects[rectOffset++];
401 SkRect& startRect = rects[rectOffset++];
402 SkRect& endRect = rects[rectOffset++];
joshualitt4f569be2015-02-27 11:41:49 -0800403
404 bool hasStartRect = false;
405 // If we are using AA, check to see if we are drawing a partial dash at the start. If so
406 // draw it separately here and adjust our start point accordingly
407 if (useAA) {
joshualitt144c3c82015-11-30 12:30:13 -0800408 if (draw.fPhase > 0 && draw.fPhase < draw.fIntervals[0]) {
joshualitt4f569be2015-02-27 11:41:49 -0800409 SkPoint startPts[2];
joshualitt144c3c82015-11-30 12:30:13 -0800410 startPts[0] = draw.fPtsRot[0];
joshualitt4f569be2015-02-27 11:41:49 -0800411 startPts[1].fY = startPts[0].fY;
joshualitt144c3c82015-11-30 12:30:13 -0800412 startPts[1].fX = SkMinScalar(startPts[0].fX + draw.fIntervals[0] - draw.fPhase,
413 draw.fPtsRot[1].fX);
joshualitt4f569be2015-02-27 11:41:49 -0800414 startRect.set(startPts, 2);
415 startRect.outset(strokeAdj, halfSrcStroke);
416
417 hasStartRect = true;
joshualitt144c3c82015-11-30 12:30:13 -0800418 startAdj = draw.fIntervals[0] + draw.fIntervals[1] - draw.fPhase;
joshualitt4f569be2015-02-27 11:41:49 -0800419 }
420 }
421
422 // adjustments for start and end of bounding rect so we only draw dash intervals
423 // contained in the original line segment.
joshualitt144c3c82015-11-30 12:30:13 -0800424 startAdj += calc_start_adjustment(draw.fIntervals, draw.fPhase);
joshualitt4f569be2015-02-27 11:41:49 -0800425 if (startAdj != 0) {
joshualitt144c3c82015-11-30 12:30:13 -0800426 draw.fPtsRot[0].fX += startAdj;
427 draw.fPhase = 0;
joshualitt4f569be2015-02-27 11:41:49 -0800428 }
429 SkScalar endingInterval = 0;
joshualitt144c3c82015-11-30 12:30:13 -0800430 SkScalar endAdj = calc_end_adjustment(draw.fIntervals, draw.fPtsRot, draw.fPhase,
joshualitt4f569be2015-02-27 11:41:49 -0800431 &endingInterval);
joshualitt144c3c82015-11-30 12:30:13 -0800432 draw.fPtsRot[1].fX -= endAdj;
433 if (draw.fPtsRot[0].fX >= draw.fPtsRot[1].fX) {
joshualitt4f569be2015-02-27 11:41:49 -0800434 lineDone = true;
435 }
436
437 bool hasEndRect = false;
438 // If we are using AA, check to see if we are drawing a partial dash at then end. If so
439 // draw it separately here and adjust our end point accordingly
440 if (useAA && !lineDone) {
441 // If we adjusted the end then we will not be drawing a partial dash at the end.
442 // If we didn't adjust the end point then we just need to make sure the ending
443 // dash isn't a full dash
joshualitt144c3c82015-11-30 12:30:13 -0800444 if (0 == endAdj && endingInterval != draw.fIntervals[0]) {
joshualitt4f569be2015-02-27 11:41:49 -0800445 SkPoint endPts[2];
joshualitt144c3c82015-11-30 12:30:13 -0800446 endPts[1] = draw.fPtsRot[1];
joshualitt4f569be2015-02-27 11:41:49 -0800447 endPts[0].fY = endPts[1].fY;
448 endPts[0].fX = endPts[1].fX - endingInterval;
449
450 endRect.set(endPts, 2);
451 endRect.outset(strokeAdj, halfSrcStroke);
452
453 hasEndRect = true;
joshualitt144c3c82015-11-30 12:30:13 -0800454 endAdj = endingInterval + draw.fIntervals[1];
joshualitt4f569be2015-02-27 11:41:49 -0800455
joshualitt144c3c82015-11-30 12:30:13 -0800456 draw.fPtsRot[1].fX -= endAdj;
457 if (draw.fPtsRot[0].fX >= draw.fPtsRot[1].fX) {
joshualitt4f569be2015-02-27 11:41:49 -0800458 lineDone = true;
459 }
460 }
461 }
462
463 if (startAdj != 0) {
joshualitt144c3c82015-11-30 12:30:13 -0800464 draw.fPhase = 0;
joshualitt4f569be2015-02-27 11:41:49 -0800465 }
466
467 // Change the dashing info from src space into device space
joshualitt144c3c82015-11-30 12:30:13 -0800468 SkScalar* devIntervals = draw.fIntervals;
469 devIntervals[0] = draw.fIntervals[0] * args.fParallelScale;
470 devIntervals[1] = draw.fIntervals[1] * args.fParallelScale;
471 SkScalar devPhase = draw.fPhase * args.fParallelScale;
joshualitt4f569be2015-02-27 11:41:49 -0800472 SkScalar strokeWidth = args.fSrcStrokeWidth * args.fPerpendicularScale;
473
senorblancof3c2c462015-04-20 14:44:26 -0700474 if ((strokeWidth < 1.f && useAA) || 0.f == strokeWidth) {
joshualitt4f569be2015-02-27 11:41:49 -0800475 strokeWidth = 1.f;
476 }
477
478 SkScalar halfDevStroke = strokeWidth * 0.5f;
479
480 if (SkPaint::kSquare_Cap == cap && 0 != args.fSrcStrokeWidth) {
481 // add cap to on interval and remove from off interval
482 devIntervals[0] += strokeWidth;
483 devIntervals[1] -= strokeWidth;
484 }
485 SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
486
senorblancof3c2c462015-04-20 14:44:26 -0700487 // For EdgeAA, we bloat in X & Y for both square and round caps.
488 // For MSAA, we don't bloat at all for square caps, and bloat in Y only for round caps.
489 SkScalar devBloatX = this->aaMode() == kEdgeAA_DashAAMode ? 0.5f : 0.0f;
490 SkScalar devBloatY = (SkPaint::kRound_Cap == cap && this->aaMode() == kMSAA_DashAAMode)
491 ? 0.5f : devBloatX;
joshualitt4f569be2015-02-27 11:41:49 -0800492
senorblancof3c2c462015-04-20 14:44:26 -0700493 SkScalar bloatX = devBloatX / args.fParallelScale;
494 SkScalar bloatY = devBloatY / args.fPerpendicularScale;
joshualitt4f569be2015-02-27 11:41:49 -0800495
496 if (devIntervals[1] <= 0.f && useAA) {
497 // Case when we end up drawing a solid AA rect
498 // Reset the start rect to draw this single solid rect
499 // but it requires to upload a new intervals uniform so we can mimic
500 // one giant dash
joshualitt144c3c82015-11-30 12:30:13 -0800501 draw.fPtsRot[0].fX -= hasStartRect ? startAdj : 0;
502 draw.fPtsRot[1].fX += hasEndRect ? endAdj : 0;
503 startRect.set(draw.fPtsRot, 2);
joshualitt4f569be2015-02-27 11:41:49 -0800504 startRect.outset(strokeAdj, halfSrcStroke);
505 hasStartRect = true;
506 hasEndRect = false;
507 lineDone = true;
508
509 SkPoint devicePts[2];
joshualitt144c3c82015-11-30 12:30:13 -0800510 args.fViewMatrix.mapPoints(devicePts, draw.fPtsRot, 2);
joshualitt4f569be2015-02-27 11:41:49 -0800511 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
512 if (hasCap) {
513 lineLength += 2.f * halfDevStroke;
514 }
515 devIntervals[0] = lineLength;
516 }
517
518 totalRectCount += !lineDone ? 1 : 0;
519 totalRectCount += hasStartRect ? 1 : 0;
520 totalRectCount += hasEndRect ? 1 : 0;
521
522 if (SkPaint::kRound_Cap == cap && 0 != args.fSrcStrokeWidth) {
523 // need to adjust this for round caps to correctly set the dashPos attrib on
524 // vertices
525 startOffset -= halfDevStroke;
526 }
527
joshualitt4f569be2015-02-27 11:41:49 -0800528 if (!lineDone) {
529 SkPoint devicePts[2];
joshualitt144c3c82015-11-30 12:30:13 -0800530 args.fViewMatrix.mapPoints(devicePts, draw.fPtsRot, 2);
joshualitt4f569be2015-02-27 11:41:49 -0800531 draw.fLineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
532 if (hasCap) {
533 draw.fLineLength += 2.f * halfDevStroke;
534 }
535
joshualitt144c3c82015-11-30 12:30:13 -0800536 bounds.set(draw.fPtsRot[0].fX, draw.fPtsRot[0].fY,
537 draw.fPtsRot[1].fX, draw.fPtsRot[1].fY);
joshualitt4f569be2015-02-27 11:41:49 -0800538 bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke);
539 }
540
541 if (hasStartRect) {
542 SkASSERT(useAA); // so that we know bloatX and bloatY have been set
543 startRect.outset(bloatX, bloatY);
544 }
545
546 if (hasEndRect) {
547 SkASSERT(useAA); // so that we know bloatX and bloatY have been set
548 endRect.outset(bloatX, bloatY);
549 }
550
551 draw.fStartOffset = startOffset;
senorblancof3c2c462015-04-20 14:44:26 -0700552 draw.fDevBloatX = devBloatX;
553 draw.fDevBloatY = devBloatY;
joshualitt4f569be2015-02-27 11:41:49 -0800554 draw.fHalfDevStroke = halfDevStroke;
555 draw.fStrokeWidth = strokeWidth;
556 draw.fHasStartRect = hasStartRect;
557 draw.fLineDone = lineDone;
558 draw.fHasEndRect = hasEndRect;
559 }
560
bsalomonb5238a72015-05-05 07:49:49 -0700561 if (!totalRectCount) {
562 return;
563 }
bsalomon8415abe2015-05-04 11:41:41 -0700564
bsalomonb5238a72015-05-05 07:49:49 -0700565 QuadHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700566 void* vertices = helper.init(target, gp->getVertexStride(), totalRectCount);
bsalomonb5238a72015-05-05 07:49:49 -0700567 if (!vertices) {
joshualitt4b31de82015-03-05 14:33:41 -0800568 return;
569 }
570
joshualitt4f569be2015-02-27 11:41:49 -0800571 int curVIdx = 0;
572 int rectIndex = 0;
573 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800574 const Geometry& geom = fGeoData[i];
joshualitt4f569be2015-02-27 11:41:49 -0800575
576 if (!draws[i].fLineDone) {
577 if (fullDash) {
bsalomonb5238a72015-05-05 07:49:49 -0700578 setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
senorblancof3c2c462015-04-20 14:44:26 -0700579 draws[i].fStartOffset, draws[i].fDevBloatX,
580 draws[i].fDevBloatY, draws[i].fLineLength,
joshualitt144c3c82015-11-30 12:30:13 -0800581 draws[i].fHalfDevStroke, draws[i].fIntervals[0],
582 draws[i].fIntervals[1], draws[i].fStrokeWidth,
joshualitt4f569be2015-02-27 11:41:49 -0800583 capType, gp->getVertexStride());
584 } else {
585 SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
586 SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
bsalomonb5238a72015-05-05 07:49:49 -0700587 setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
joshualitt4f569be2015-02-27 11:41:49 -0800588 }
589 curVIdx += 4;
590 }
591 rectIndex++;
592
593 if (draws[i].fHasStartRect) {
594 if (fullDash) {
bsalomonb5238a72015-05-05 07:49:49 -0700595 setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
senorblancof3c2c462015-04-20 14:44:26 -0700596 draws[i].fStartOffset, draws[i].fDevBloatX,
joshualitt144c3c82015-11-30 12:30:13 -0800597 draws[i].fDevBloatY, draws[i].fIntervals[0],
598 draws[i].fHalfDevStroke, draws[i].fIntervals[0],
599 draws[i].fIntervals[1], draws[i].fStrokeWidth, capType,
joshualitt4f569be2015-02-27 11:41:49 -0800600 gp->getVertexStride());
601 } else {
602 SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
603 SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
bsalomonb5238a72015-05-05 07:49:49 -0700604 setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
joshualitt4f569be2015-02-27 11:41:49 -0800605 }
joshualitt4f569be2015-02-27 11:41:49 -0800606 curVIdx += 4;
607 }
608 rectIndex++;
609
610 if (draws[i].fHasEndRect) {
611 if (fullDash) {
bsalomonb5238a72015-05-05 07:49:49 -0700612 setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
senorblancof3c2c462015-04-20 14:44:26 -0700613 draws[i].fStartOffset, draws[i].fDevBloatX,
joshualitt144c3c82015-11-30 12:30:13 -0800614 draws[i].fDevBloatY, draws[i].fIntervals[0],
615 draws[i].fHalfDevStroke, draws[i].fIntervals[0],
616 draws[i].fIntervals[1], draws[i].fStrokeWidth, capType,
joshualitt4f569be2015-02-27 11:41:49 -0800617 gp->getVertexStride());
618 } else {
619 SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
620 SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
bsalomonb5238a72015-05-05 07:49:49 -0700621 setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
joshualitt4f569be2015-02-27 11:41:49 -0800622 }
623 curVIdx += 4;
624 }
625 rectIndex++;
626 }
bsalomonb5238a72015-05-05 07:49:49 -0700627 SkASSERT(0 == (curVIdx % 4) && (curVIdx / 4) == totalRectCount);
bsalomon342bfc22016-04-01 06:06:20 -0700628 helper.recordDraw(target, gp);
joshualitt4f569be2015-02-27 11:41:49 -0800629 }
630
bsalomoncb02b382015-08-12 11:14:50 -0700631 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700632 DashBatch* that = t->cast<DashBatch>();
633 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
634 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700635 return false;
636 }
637
senorblancof3c2c462015-04-20 14:44:26 -0700638 if (this->aaMode() != that->aaMode()) {
joshualitt4f569be2015-02-27 11:41:49 -0800639 return false;
640 }
641
642 if (this->fullDash() != that->fullDash()) {
643 return false;
644 }
645
646 if (this->cap() != that->cap()) {
647 return false;
648 }
649
650 // TODO vertex color
651 if (this->color() != that->color()) {
652 return false;
653 }
654
655 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
656 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
657 return false;
658 }
659
660 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700661 this->joinBounds(that->bounds());
joshualitt4f569be2015-02-27 11:41:49 -0800662 return true;
663 }
664
665 GrColor color() const { return fBatch.fColor; }
666 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
667 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
senorblancof3c2c462015-04-20 14:44:26 -0700668 DashAAMode aaMode() const { return fBatch.fAAMode; }
joshualitt4f569be2015-02-27 11:41:49 -0800669 bool fullDash() const { return fBatch.fFullDash; }
670 SkPaint::Cap cap() const { return fBatch.fCap; }
joshualittb8c241a2015-05-19 08:23:30 -0700671 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
joshualitt4f569be2015-02-27 11:41:49 -0800672
673 struct BatchTracker {
674 GrColor fColor;
675 bool fUsesLocalCoords;
676 bool fColorIgnored;
677 bool fCoverageIgnored;
678 SkPaint::Cap fCap;
senorblancof3c2c462015-04-20 14:44:26 -0700679 DashAAMode fAAMode;
joshualitt4f569be2015-02-27 11:41:49 -0800680 bool fFullDash;
681 };
682
683 static const int kVertsPerDash = 4;
684 static const int kIndicesPerDash = 6;
685
686 BatchTracker fBatch;
687 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700688
689 typedef GrVertexBatch INHERITED;
joshualitt4f569be2015-02-27 11:41:49 -0800690};
691
bsalomonabd30f52015-08-13 13:34:48 -0700692static GrDrawBatch* create_batch(GrColor color, const SkMatrix& viewMatrix, const SkPoint pts[2],
693 bool useAA, const GrStrokeInfo& strokeInfo, bool msaaRT) {
kkinnunen261694c2015-05-05 08:00:10 -0700694 const SkScalar* intervals = strokeInfo.getDashIntervals();
695 SkScalar phase = strokeInfo.getDashPhase();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000696
kkinnunend156d362015-05-18 22:23:54 -0700697 SkPaint::Cap cap = strokeInfo.getCap();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000698
joshualitt4f569be2015-02-27 11:41:49 -0800699 DashBatch::Geometry geometry;
kkinnunend156d362015-05-18 22:23:54 -0700700 geometry.fSrcStrokeWidth = strokeInfo.getWidth();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000701
702 // the phase should be normalized to be [0, sum of all intervals)
kkinnunen261694c2015-05-05 08:00:10 -0700703 SkASSERT(phase >= 0 && phase < intervals[0] + intervals[1]);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000704
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000705 // 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 +0000706 if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
egdaniele61c4112014-06-12 10:24:21 -0700707 SkMatrix rotMatrix;
joshualitt4f569be2015-02-27 11:41:49 -0800708 align_to_x_axis(pts, &rotMatrix, geometry.fPtsRot);
709 if(!rotMatrix.invert(&geometry.fSrcRotInv)) {
tfarina38406c82014-10-31 07:11:12 -0700710 SkDebugf("Failed to create invertible rotation matrix!\n");
halcanary96fcdcc2015-08-27 07:41:13 -0700711 return nullptr;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000712 }
713 } else {
joshualitt4f569be2015-02-27 11:41:49 -0800714 geometry.fSrcRotInv.reset();
715 memcpy(geometry.fPtsRot, pts, 2 * sizeof(SkPoint));
716 }
717
718 // Scale corrections of intervals and stroke from view matrix
719 calc_dash_scaling(&geometry.fParallelScale, &geometry.fPerpendicularScale, viewMatrix,
720 geometry.fPtsRot);
721
kkinnunen261694c2015-05-05 08:00:10 -0700722 SkScalar offInterval = intervals[1] * geometry.fParallelScale;
joshualitt4f569be2015-02-27 11:41:49 -0800723 SkScalar strokeWidth = geometry.fSrcStrokeWidth * geometry.fPerpendicularScale;
724
725 if (SkPaint::kSquare_Cap == cap && 0 != geometry.fSrcStrokeWidth) {
726 // add cap to on interveal and remove from off interval
727 offInterval -= strokeWidth;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000728 }
729
joshualittfa2008f2015-04-29 11:32:05 -0700730 DashAAMode aaMode = msaaRT ? kMSAA_DashAAMode :
731 useAA ? kEdgeAA_DashAAMode : kBW_DashAAMode;
senorblancof3c2c462015-04-20 14:44:26 -0700732
joshualitt4f569be2015-02-27 11:41:49 -0800733 // TODO we can do a real rect call if not using fulldash(ie no off interval, not using AA)
senorblancof3c2c462015-04-20 14:44:26 -0700734 bool fullDash = offInterval > 0.f || aaMode != kBW_DashAAMode;
skia.committer@gmail.com3b9e8be2014-05-20 03:05:34 +0000735
joshualitt4f569be2015-02-27 11:41:49 -0800736 geometry.fColor = color;
737 geometry.fViewMatrix = viewMatrix;
kkinnunen261694c2015-05-05 08:00:10 -0700738 geometry.fPhase = phase;
739 geometry.fIntervals[0] = intervals[0];
740 geometry.fIntervals[1] = intervals[1];
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000741
joshualittfa2008f2015-04-29 11:32:05 -0700742 return DashBatch::Create(geometry, cap, aaMode, fullDash);
743}
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000744
robertphillips73c4e642016-03-02 11:36:59 -0800745GrDrawBatch* GrDashingEffect::CreateDashLineBatch(GrColor color,
746 const SkMatrix& viewMatrix,
747 const SkPoint pts[2],
748 bool useAA,
749 bool msaaIsEnabled,
750 const GrStrokeInfo& strokeInfo) {
751 return create_batch(color, viewMatrix, pts, useAA, strokeInfo, msaaIsEnabled);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000752}
753
754//////////////////////////////////////////////////////////////////////////////
755
egdanielf767e792014-07-02 06:21:32 -0700756class GLDashingCircleEffect;
joshualitt9b989322014-12-15 14:16:27 -0800757
egdanielf767e792014-07-02 06:21:32 -0700758/*
759 * This effect will draw a dotted line (defined as a dashed lined with round caps and no on
760 * interval). The radius of the dots is given by the strokeWidth and the spacing by the DashInfo.
761 * Both of the previous two parameters are in device space. This effect also requires the setting of
762 * a vec2 vertex attribute for the the four corners of the bounding rect. This attribute is the
763 * "dash position" of each vertex. In other words it is the vertex coords (in device space) if we
764 * transform the line to be horizontal, with the start of line at the origin then shifted to the
765 * right by half the off interval. The line then goes in the positive x direction.
766 */
joshualitt249af152014-09-15 11:41:13 -0700767class DashingCircleEffect : public GrGeometryProcessor {
egdanielf767e792014-07-02 06:21:32 -0700768public:
769 typedef SkPathEffect::DashInfo DashInfo;
770
joshualitt2e3b3e32014-12-09 13:31:14 -0800771 static GrGeometryProcessor* Create(GrColor,
senorblancof3c2c462015-04-20 14:44:26 -0700772 DashAAMode aaMode,
joshualittb8c241a2015-05-19 08:23:30 -0700773 const SkMatrix& localMatrix,
774 bool usesLocalCoords);
egdanielf767e792014-07-02 06:21:32 -0700775
mtklein36352bf2015-03-25 18:17:31 -0700776 const char* name() const override { return "DashingCircleEffect"; }
egdanielf767e792014-07-02 06:21:32 -0700777
joshualitt71c92602015-01-14 08:12:47 -0800778 const Attribute* inPosition() const { return fInPosition; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800779
joshualitt5224ba72015-02-03 15:07:51 -0800780 const Attribute* inDashParams() const { return fInDashParams; }
781
782 const Attribute* inCircleParams() const { return fInCircleParams; }
joshualitt249af152014-09-15 11:41:13 -0700783
senorblancof3c2c462015-04-20 14:44:26 -0700784 DashAAMode aaMode() const { return fAAMode; }
egdanielf767e792014-07-02 06:21:32 -0700785
joshualitt88c23fc2015-05-13 14:18:07 -0700786 GrColor color() const { return fColor; }
787
joshualittb8c241a2015-05-19 08:23:30 -0700788 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
789
joshualitte3ababe2015-05-15 07:56:07 -0700790 const SkMatrix& localMatrix() const { return fLocalMatrix; }
791
joshualittb8c241a2015-05-19 08:23:30 -0700792 bool usesLocalCoords() const { return fUsesLocalCoords; }
793
egdaniel57d3b032015-11-13 11:57:27 -0800794 void getGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder* b) const override;
egdanielf767e792014-07-02 06:21:32 -0700795
egdaniel57d3b032015-11-13 11:57:27 -0800796 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override;
egdanielf767e792014-07-02 06:21:32 -0700797
798private:
joshualittb8c241a2015-05-19 08:23:30 -0700799 DashingCircleEffect(GrColor, DashAAMode aaMode, const SkMatrix& localMatrix,
800 bool usesLocalCoords);
egdanielf767e792014-07-02 06:21:32 -0700801
joshualitt88c23fc2015-05-13 14:18:07 -0700802 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700803 SkMatrix fLocalMatrix;
joshualittb8c241a2015-05-19 08:23:30 -0700804 bool fUsesLocalCoords;
senorblancof3c2c462015-04-20 14:44:26 -0700805 DashAAMode fAAMode;
joshualitt5224ba72015-02-03 15:07:51 -0800806 const Attribute* fInPosition;
807 const Attribute* fInDashParams;
808 const Attribute* fInCircleParams;
egdanielf767e792014-07-02 06:21:32 -0700809
joshualittb0a8a372014-09-23 09:50:21 -0700810 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
egdanielf767e792014-07-02 06:21:32 -0700811
joshualitt249af152014-09-15 11:41:13 -0700812 typedef GrGeometryProcessor INHERITED;
egdanielf767e792014-07-02 06:21:32 -0700813};
814
815//////////////////////////////////////////////////////////////////////////////
816
egdaniele659a582015-11-13 09:55:43 -0800817class GLDashingCircleEffect : public GrGLSLGeometryProcessor {
egdanielf767e792014-07-02 06:21:32 -0700818public:
joshualitt465283c2015-09-11 08:19:35 -0700819 GLDashingCircleEffect();
egdanielf767e792014-07-02 06:21:32 -0700820
mtklein36352bf2015-03-25 18:17:31 -0700821 void onEmitCode(EmitArgs&, GrGPArgs*) override;
egdanielf767e792014-07-02 06:21:32 -0700822
joshualitt87f48d92014-12-04 10:41:40 -0800823 static inline void GenKey(const GrGeometryProcessor&,
jvanverthcfc18862015-04-28 08:48:20 -0700824 const GrGLSLCaps&,
joshualitt87f48d92014-12-04 10:41:40 -0800825 GrProcessorKeyBuilder*);
egdanielf767e792014-07-02 06:21:32 -0700826
egdaniel018fb622015-10-28 07:26:40 -0700827 void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override;
egdanielf767e792014-07-02 06:21:32 -0700828
joshualitte3ababe2015-05-15 07:56:07 -0700829 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700830 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700831 int index,
832 const SkTArray<const GrCoordTransform*, true>& transforms) override {
833 this->setTransformDataHelper<DashingCircleEffect>(primProc, pdman, index, transforms);
834 }
835
egdanielf767e792014-07-02 06:21:32 -0700836private:
joshualitt9b989322014-12-15 14:16:27 -0800837 UniformHandle fParamUniform;
838 UniformHandle fColorUniform;
839 GrColor fColor;
840 SkScalar fPrevRadius;
841 SkScalar fPrevCenterX;
842 SkScalar fPrevIntervalLength;
egdaniele659a582015-11-13 09:55:43 -0800843 typedef GrGLSLGeometryProcessor INHERITED;
egdanielf767e792014-07-02 06:21:32 -0700844};
845
joshualitt465283c2015-09-11 08:19:35 -0700846GLDashingCircleEffect::GLDashingCircleEffect() {
joshualitt9b989322014-12-15 14:16:27 -0800847 fColor = GrColor_ILLEGAL;
egdanielf767e792014-07-02 06:21:32 -0700848 fPrevRadius = SK_ScalarMin;
849 fPrevCenterX = SK_ScalarMin;
850 fPrevIntervalLength = SK_ScalarMax;
851}
852
robertphillips46d36f02015-01-18 08:14:14 -0800853void GLDashingCircleEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
joshualittc369e7c2014-10-22 10:56:26 -0700854 const DashingCircleEffect& dce = args.fGP.cast<DashingCircleEffect>();
egdaniel4ca2e602015-11-18 08:01:26 -0800855 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800856 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800857 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800858
joshualittabb52a12015-01-13 15:02:10 -0800859 // emit attributes
egdaniel0eafe792015-11-20 14:01:22 -0800860 varyingHandler->emitAttributes(dce);
joshualittabb52a12015-01-13 15:02:10 -0800861
joshualitt5224ba72015-02-03 15:07:51 -0800862 // XY are dashPos, Z is dashInterval
egdaniel8dcdedc2015-11-11 06:27:20 -0800863 GrGLSLVertToFrag dashParams(kVec3f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800864 varyingHandler->addVarying("DashParam", &dashParams);
egdaniel4ca2e602015-11-18 08:01:26 -0800865 vertBuilder->codeAppendf("%s = %s;", dashParams.vsOut(), dce.inDashParams()->fName);
joshualitt5224ba72015-02-03 15:07:51 -0800866
senorblancof3c2c462015-04-20 14:44:26 -0700867 // x refers to circle radius - 0.5, y refers to cicle's center x coord
egdaniel8dcdedc2015-11-11 06:27:20 -0800868 GrGLSLVertToFrag circleParams(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800869 varyingHandler->addVarying("CircleParams", &circleParams);
egdaniel4ca2e602015-11-18 08:01:26 -0800870 vertBuilder->codeAppendf("%s = %s;", circleParams.vsOut(), dce.inCircleParams()->fName);
joshualitt30ba4362014-08-21 20:18:45 -0700871
cdalton85285412016-02-18 12:37:07 -0800872 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt9b989322014-12-15 14:16:27 -0800873 // Setup pass through color
joshualittb8c241a2015-05-19 08:23:30 -0700874 if (!dce.colorIgnored()) {
egdaniel7ea439b2015-12-03 09:20:44 -0800875 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform);
joshualittb8c241a2015-05-19 08:23:30 -0700876 }
joshualitt9b989322014-12-15 14:16:27 -0800877
joshualittabb52a12015-01-13 15:02:10 -0800878 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800879 this->setupPosition(vertBuilder, gpArgs, dce.inPosition()->fName);
joshualitt4973d9d2014-11-08 09:24:25 -0800880
joshualittabb52a12015-01-13 15:02:10 -0800881 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800882 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800883 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800884 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800885 gpArgs->fPositionVar,
886 dce.inPosition()->fName,
887 dce.localMatrix(),
888 args.fTransformsIn,
889 args.fTransformsOut);
joshualittabb52a12015-01-13 15:02:10 -0800890
egdanielf767e792014-07-02 06:21:32 -0700891 // transforms all points so that we can compare them to our test circle
egdaniel4ca2e602015-11-18 08:01:26 -0800892 fragBuilder->codeAppendf("float xShifted = %s.x - floor(%s.x / %s.z) * %s.z;",
893 dashParams.fsIn(), dashParams.fsIn(), dashParams.fsIn(),
894 dashParams.fsIn());
895 fragBuilder->codeAppendf("vec2 fragPosShifted = vec2(xShifted, %s.y);", dashParams.fsIn());
896 fragBuilder->codeAppendf("vec2 center = vec2(%s.y, 0.0);", circleParams.fsIn());
897 fragBuilder->codeAppend("float dist = length(center - fragPosShifted);");
senorblancof3c2c462015-04-20 14:44:26 -0700898 if (dce.aaMode() != kBW_DashAAMode) {
egdaniel4ca2e602015-11-18 08:01:26 -0800899 fragBuilder->codeAppendf("float diff = dist - %s.x;", circleParams.fsIn());
900 fragBuilder->codeAppend("diff = 1.0 - diff;");
901 fragBuilder->codeAppend("float alpha = clamp(diff, 0.0, 1.0);");
egdanielf767e792014-07-02 06:21:32 -0700902 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800903 fragBuilder->codeAppendf("float alpha = 1.0;");
904 fragBuilder->codeAppendf("alpha *= dist < %s.x + 0.5 ? 1.0 : 0.0;", circleParams.fsIn());
egdanielf767e792014-07-02 06:21:32 -0700905 }
egdaniel4ca2e602015-11-18 08:01:26 -0800906 fragBuilder->codeAppendf("%s = vec4(alpha);", args.fOutputCoverage);
egdanielf767e792014-07-02 06:21:32 -0700907}
908
egdaniel018fb622015-10-28 07:26:40 -0700909void GLDashingCircleEffect::setData(const GrGLSLProgramDataManager& pdman,
joshualitt465283c2015-09-11 08:19:35 -0700910 const GrPrimitiveProcessor& processor) {
joshualittb8c241a2015-05-19 08:23:30 -0700911 const DashingCircleEffect& dce = processor.cast<DashingCircleEffect>();
912 if (dce.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700913 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700914 GrColorToRGBAFloat(dce.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800915 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700916 fColor = dce.color();
joshualitt9b989322014-12-15 14:16:27 -0800917 }
egdanielf767e792014-07-02 06:21:32 -0700918}
919
robertphillips46d36f02015-01-18 08:14:14 -0800920void GLDashingCircleEffect::GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700921 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700922 GrProcessorKeyBuilder* b) {
robertphillips46d36f02015-01-18 08:14:14 -0800923 const DashingCircleEffect& dce = gp.cast<DashingCircleEffect>();
924 uint32_t key = 0;
joshualittb8c241a2015-05-19 08:23:30 -0700925 key |= dce.usesLocalCoords() && dce.localMatrix().hasPerspective() ? 0x1 : 0x0;
926 key |= dce.colorIgnored() ? 0x2 : 0x0;
senorblancof3c2c462015-04-20 14:44:26 -0700927 key |= dce.aaMode() << 8;
joshualittb8c241a2015-05-19 08:23:30 -0700928 b->add32(key);
egdanielf767e792014-07-02 06:21:32 -0700929}
930
931//////////////////////////////////////////////////////////////////////////////
932
joshualitt2e3b3e32014-12-09 13:31:14 -0800933GrGeometryProcessor* DashingCircleEffect::Create(GrColor color,
senorblancof3c2c462015-04-20 14:44:26 -0700934 DashAAMode aaMode,
joshualittb8c241a2015-05-19 08:23:30 -0700935 const SkMatrix& localMatrix,
936 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -0700937 return new DashingCircleEffect(color, aaMode, localMatrix, usesLocalCoords);
egdanielf767e792014-07-02 06:21:32 -0700938}
939
egdaniel57d3b032015-11-13 11:57:27 -0800940void DashingCircleEffect::getGLSLProcessorKey(const GrGLSLCaps& caps,
941 GrProcessorKeyBuilder* b) const {
joshualitt465283c2015-09-11 08:19:35 -0700942 GLDashingCircleEffect::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800943}
944
egdaniel57d3b032015-11-13 11:57:27 -0800945GrGLSLPrimitiveProcessor* DashingCircleEffect::createGLSLInstance(const GrGLSLCaps&) const {
joshualitt465283c2015-09-11 08:19:35 -0700946 return new GLDashingCircleEffect();
egdanielf767e792014-07-02 06:21:32 -0700947}
948
joshualitt2e3b3e32014-12-09 13:31:14 -0800949DashingCircleEffect::DashingCircleEffect(GrColor color,
senorblancof3c2c462015-04-20 14:44:26 -0700950 DashAAMode aaMode,
joshualittb8c241a2015-05-19 08:23:30 -0700951 const SkMatrix& localMatrix,
952 bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700953 : fColor(color)
954 , fLocalMatrix(localMatrix)
joshualittb8c241a2015-05-19 08:23:30 -0700955 , fUsesLocalCoords(usesLocalCoords)
joshualitt88c23fc2015-05-13 14:18:07 -0700956 , fAAMode(aaMode) {
joshualitteb2a6762014-12-04 11:35:33 -0800957 this->initClassID<DashingCircleEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800958 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
joshualitt5224ba72015-02-03 15:07:51 -0800959 fInDashParams = &this->addVertexAttrib(Attribute("inDashParams", kVec3f_GrVertexAttribType));
960 fInCircleParams = &this->addVertexAttrib(Attribute("inCircleParams",
961 kVec2f_GrVertexAttribType));
egdanielf767e792014-07-02 06:21:32 -0700962}
963
joshualittb0a8a372014-09-23 09:50:21 -0700964GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingCircleEffect);
egdanielf767e792014-07-02 06:21:32 -0700965
bsalomonc21b09e2015-08-28 18:46:56 -0700966const GrGeometryProcessor* DashingCircleEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700967 DashAAMode aaMode = static_cast<DashAAMode>(d->fRandom->nextULessThan(kDashAAModeCount));
968 return DashingCircleEffect::Create(GrRandomColor(d->fRandom),
969 aaMode, GrTest::TestMatrix(d->fRandom),
970 d->fRandom->nextBool());
egdanielf767e792014-07-02 06:21:32 -0700971}
972
973//////////////////////////////////////////////////////////////////////////////
974
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000975class GLDashingLineEffect;
976
egdanielf767e792014-07-02 06:21:32 -0700977/*
978 * This effect will draw a dashed line. The width of the dash is given by the strokeWidth and the
979 * length and spacing by the DashInfo. Both of the previous two parameters are in device space.
980 * This effect also requires the setting of a vec2 vertex attribute for the the four corners of the
981 * bounding rect. This attribute is the "dash position" of each vertex. In other words it is the
982 * vertex coords (in device space) if we transform the line to be horizontal, with the start of
983 * line at the origin then shifted to the right by half the off interval. The line then goes in the
984 * positive x direction.
985 */
joshualitt249af152014-09-15 11:41:13 -0700986class DashingLineEffect : public GrGeometryProcessor {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000987public:
988 typedef SkPathEffect::DashInfo DashInfo;
989
joshualitt2e3b3e32014-12-09 13:31:14 -0800990 static GrGeometryProcessor* Create(GrColor,
senorblancof3c2c462015-04-20 14:44:26 -0700991 DashAAMode aaMode,
joshualittb8c241a2015-05-19 08:23:30 -0700992 const SkMatrix& localMatrix,
993 bool usesLocalCoords);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000994
mtklein36352bf2015-03-25 18:17:31 -0700995 const char* name() const override { return "DashingEffect"; }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000996
joshualitt71c92602015-01-14 08:12:47 -0800997 const Attribute* inPosition() const { return fInPosition; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800998
joshualitt5224ba72015-02-03 15:07:51 -0800999 const Attribute* inDashParams() const { return fInDashParams; }
1000
1001 const Attribute* inRectParams() const { return fInRectParams; }
joshualitt249af152014-09-15 11:41:13 -07001002
senorblancof3c2c462015-04-20 14:44:26 -07001003 DashAAMode aaMode() const { return fAAMode; }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001004
joshualitt88c23fc2015-05-13 14:18:07 -07001005 GrColor color() const { return fColor; }
1006
joshualittb8c241a2015-05-19 08:23:30 -07001007 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
1008
joshualitte3ababe2015-05-15 07:56:07 -07001009 const SkMatrix& localMatrix() const { return fLocalMatrix; }
1010
joshualittb8c241a2015-05-19 08:23:30 -07001011 bool usesLocalCoords() const { return fUsesLocalCoords; }
1012
egdaniel57d3b032015-11-13 11:57:27 -08001013 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001014
egdaniel57d3b032015-11-13 11:57:27 -08001015 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001016
1017private:
joshualittb8c241a2015-05-19 08:23:30 -07001018 DashingLineEffect(GrColor, DashAAMode aaMode, const SkMatrix& localMatrix,
1019 bool usesLocalCoords);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001020
joshualitt88c23fc2015-05-13 14:18:07 -07001021 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -07001022 SkMatrix fLocalMatrix;
joshualittb8c241a2015-05-19 08:23:30 -07001023 bool fUsesLocalCoords;
senorblancof3c2c462015-04-20 14:44:26 -07001024 DashAAMode fAAMode;
joshualitt5224ba72015-02-03 15:07:51 -08001025 const Attribute* fInPosition;
1026 const Attribute* fInDashParams;
1027 const Attribute* fInRectParams;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001028
joshualittb0a8a372014-09-23 09:50:21 -07001029 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001030
joshualitt249af152014-09-15 11:41:13 -07001031 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001032};
1033
1034//////////////////////////////////////////////////////////////////////////////
1035
egdaniele659a582015-11-13 09:55:43 -08001036class GLDashingLineEffect : public GrGLSLGeometryProcessor {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001037public:
joshualitt465283c2015-09-11 08:19:35 -07001038 GLDashingLineEffect();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001039
mtklein36352bf2015-03-25 18:17:31 -07001040 void onEmitCode(EmitArgs&, GrGPArgs*) override;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001041
joshualitt87f48d92014-12-04 10:41:40 -08001042 static inline void GenKey(const GrGeometryProcessor&,
jvanverthcfc18862015-04-28 08:48:20 -07001043 const GrGLSLCaps&,
joshualitt87f48d92014-12-04 10:41:40 -08001044 GrProcessorKeyBuilder*);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001045
egdaniel018fb622015-10-28 07:26:40 -07001046 void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001047
joshualitte3ababe2015-05-15 07:56:07 -07001048 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -07001049 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -07001050 int index,
1051 const SkTArray<const GrCoordTransform*, true>& transforms) override {
1052 this->setTransformDataHelper<DashingLineEffect>(primProc, pdman, index, transforms);
1053 }
1054
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001055private:
joshualitt9b989322014-12-15 14:16:27 -08001056 GrColor fColor;
joshualitt9b989322014-12-15 14:16:27 -08001057 UniformHandle fColorUniform;
egdaniele659a582015-11-13 09:55:43 -08001058 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001059};
1060
joshualitt465283c2015-09-11 08:19:35 -07001061GLDashingLineEffect::GLDashingLineEffect() {
joshualitt9b989322014-12-15 14:16:27 -08001062 fColor = GrColor_ILLEGAL;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001063}
1064
robertphillips46d36f02015-01-18 08:14:14 -08001065void GLDashingLineEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
joshualittc369e7c2014-10-22 10:56:26 -07001066 const DashingLineEffect& de = args.fGP.cast<DashingLineEffect>();
joshualitt2dd1ae02014-12-03 06:24:10 -08001067
egdaniel4ca2e602015-11-18 08:01:26 -08001068 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -08001069 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -08001070 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -08001071
joshualittabb52a12015-01-13 15:02:10 -08001072 // emit attributes
egdaniel0eafe792015-11-20 14:01:22 -08001073 varyingHandler->emitAttributes(de);
joshualittabb52a12015-01-13 15:02:10 -08001074
joshualitt5224ba72015-02-03 15:07:51 -08001075 // XY refers to dashPos, Z is the dash interval length
egdaniel8dcdedc2015-11-11 06:27:20 -08001076 GrGLSLVertToFrag inDashParams(kVec3f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -08001077 varyingHandler->addVarying("DashParams", &inDashParams, GrSLPrecision::kHigh_GrSLPrecision);
egdaniel4ca2e602015-11-18 08:01:26 -08001078 vertBuilder->codeAppendf("%s = %s;", inDashParams.vsOut(), de.inDashParams()->fName);
joshualitt5224ba72015-02-03 15:07:51 -08001079
1080 // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5),
1081 // respectively.
egdaniel8dcdedc2015-11-11 06:27:20 -08001082 GrGLSLVertToFrag inRectParams(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -08001083 varyingHandler->addVarying("RectParams", &inRectParams, GrSLPrecision::kHigh_GrSLPrecision);
egdaniel4ca2e602015-11-18 08:01:26 -08001084 vertBuilder->codeAppendf("%s = %s;", inRectParams.vsOut(), de.inRectParams()->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -08001085
cdalton85285412016-02-18 12:37:07 -08001086 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt9b989322014-12-15 14:16:27 -08001087 // Setup pass through color
joshualittb8c241a2015-05-19 08:23:30 -07001088 if (!de.colorIgnored()) {
egdaniel7ea439b2015-12-03 09:20:44 -08001089 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform);
joshualittb8c241a2015-05-19 08:23:30 -07001090 }
1091
joshualittabb52a12015-01-13 15:02:10 -08001092 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -08001093 this->setupPosition(vertBuilder, gpArgs, de.inPosition()->fName);
joshualitt4973d9d2014-11-08 09:24:25 -08001094
joshualittabb52a12015-01-13 15:02:10 -08001095 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -08001096 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -08001097 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -08001098 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -08001099 gpArgs->fPositionVar,
1100 de.inPosition()->fName,
1101 de.localMatrix(),
1102 args.fTransformsIn,
1103 args.fTransformsOut);
joshualittabb52a12015-01-13 15:02:10 -08001104
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001105 // transforms all points so that we can compare them to our test rect
egdaniel4ca2e602015-11-18 08:01:26 -08001106 fragBuilder->codeAppendf("float xShifted = %s.x - floor(%s.x / %s.z) * %s.z;",
1107 inDashParams.fsIn(), inDashParams.fsIn(), inDashParams.fsIn(),
1108 inDashParams.fsIn());
1109 fragBuilder->codeAppendf("vec2 fragPosShifted = vec2(xShifted, %s.y);", inDashParams.fsIn());
senorblancof3c2c462015-04-20 14:44:26 -07001110 if (de.aaMode() == kEdgeAA_DashAAMode) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001111 // The amount of coverage removed in x and y by the edges is computed as a pair of negative
1112 // numbers, xSub and ySub.
egdaniel4ca2e602015-11-18 08:01:26 -08001113 fragBuilder->codeAppend("float xSub, ySub;");
1114 fragBuilder->codeAppendf("xSub = min(fragPosShifted.x - %s.x, 0.0);", inRectParams.fsIn());
1115 fragBuilder->codeAppendf("xSub += min(%s.z - fragPosShifted.x, 0.0);", inRectParams.fsIn());
1116 fragBuilder->codeAppendf("ySub = min(fragPosShifted.y - %s.y, 0.0);", inRectParams.fsIn());
1117 fragBuilder->codeAppendf("ySub += min(%s.w - fragPosShifted.y, 0.0);", inRectParams.fsIn());
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001118 // Now compute coverage in x and y and multiply them to get the fraction of the pixel
1119 // covered.
egdaniel4ca2e602015-11-18 08:01:26 -08001120 fragBuilder->codeAppendf(
1121 "float alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));");
senorblancof3c2c462015-04-20 14:44:26 -07001122 } else if (de.aaMode() == kMSAA_DashAAMode) {
1123 // For MSAA, we don't modulate the alpha by the Y distance, since MSAA coverage will handle
1124 // AA on the the top and bottom edges. The shader is only responsible for intra-dash alpha.
egdaniel4ca2e602015-11-18 08:01:26 -08001125 fragBuilder->codeAppend("float xSub;");
1126 fragBuilder->codeAppendf("xSub = min(fragPosShifted.x - %s.x, 0.0);", inRectParams.fsIn());
1127 fragBuilder->codeAppendf("xSub += min(%s.z - fragPosShifted.x, 0.0);", inRectParams.fsIn());
senorblancof3c2c462015-04-20 14:44:26 -07001128 // Now compute coverage in x to get the fraction of the pixel covered.
egdaniel4ca2e602015-11-18 08:01:26 -08001129 fragBuilder->codeAppendf("float alpha = (1.0 + max(xSub, -1.0));");
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001130 } else {
1131 // Assuming the bounding geometry is tight so no need to check y values
egdaniel4ca2e602015-11-18 08:01:26 -08001132 fragBuilder->codeAppendf("float alpha = 1.0;");
1133 fragBuilder->codeAppendf("alpha *= (fragPosShifted.x - %s.x) > -0.5 ? 1.0 : 0.0;",
1134 inRectParams.fsIn());
1135 fragBuilder->codeAppendf("alpha *= (%s.z - fragPosShifted.x) >= -0.5 ? 1.0 : 0.0;",
1136 inRectParams.fsIn());
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001137 }
egdaniel4ca2e602015-11-18 08:01:26 -08001138 fragBuilder->codeAppendf("%s = vec4(alpha);", args.fOutputCoverage);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001139}
1140
egdaniel018fb622015-10-28 07:26:40 -07001141void GLDashingLineEffect::setData(const GrGLSLProgramDataManager& pdman,
joshualitt465283c2015-09-11 08:19:35 -07001142 const GrPrimitiveProcessor& processor) {
joshualittb8c241a2015-05-19 08:23:30 -07001143 const DashingLineEffect& de = processor.cast<DashingLineEffect>();
1144 if (de.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -07001145 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -07001146 GrColorToRGBAFloat(de.color(), c);
joshualitt9b989322014-12-15 14:16:27 -08001147 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -07001148 fColor = de.color();
joshualitt9b989322014-12-15 14:16:27 -08001149 }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001150}
1151
robertphillips46d36f02015-01-18 08:14:14 -08001152void GLDashingLineEffect::GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -07001153 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -07001154 GrProcessorKeyBuilder* b) {
robertphillips46d36f02015-01-18 08:14:14 -08001155 const DashingLineEffect& de = gp.cast<DashingLineEffect>();
1156 uint32_t key = 0;
joshualittb8c241a2015-05-19 08:23:30 -07001157 key |= de.usesLocalCoords() && de.localMatrix().hasPerspective() ? 0x1 : 0x0;
1158 key |= de.colorIgnored() ? 0x2 : 0x0;
senorblancof3c2c462015-04-20 14:44:26 -07001159 key |= de.aaMode() << 8;
joshualittb8c241a2015-05-19 08:23:30 -07001160 b->add32(key);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001161}
1162
1163//////////////////////////////////////////////////////////////////////////////
skia.committer@gmail.com3b9e8be2014-05-20 03:05:34 +00001164
joshualitt2e3b3e32014-12-09 13:31:14 -08001165GrGeometryProcessor* DashingLineEffect::Create(GrColor color,
senorblancof3c2c462015-04-20 14:44:26 -07001166 DashAAMode aaMode,
joshualittb8c241a2015-05-19 08:23:30 -07001167 const SkMatrix& localMatrix,
1168 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -07001169 return new DashingLineEffect(color, aaMode, localMatrix, usesLocalCoords);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001170}
1171
egdaniel57d3b032015-11-13 11:57:27 -08001172void DashingLineEffect::getGLSLProcessorKey(const GrGLSLCaps& caps,
1173 GrProcessorKeyBuilder* b) const {
joshualitt465283c2015-09-11 08:19:35 -07001174 GLDashingLineEffect::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -08001175}
1176
egdaniel57d3b032015-11-13 11:57:27 -08001177GrGLSLPrimitiveProcessor* DashingLineEffect::createGLSLInstance(const GrGLSLCaps&) const {
joshualitt465283c2015-09-11 08:19:35 -07001178 return new GLDashingLineEffect();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001179}
1180
joshualitt2e3b3e32014-12-09 13:31:14 -08001181DashingLineEffect::DashingLineEffect(GrColor color,
senorblancof3c2c462015-04-20 14:44:26 -07001182 DashAAMode aaMode,
joshualittb8c241a2015-05-19 08:23:30 -07001183 const SkMatrix& localMatrix,
1184 bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -07001185 : fColor(color)
1186 , fLocalMatrix(localMatrix)
joshualittb8c241a2015-05-19 08:23:30 -07001187 , fUsesLocalCoords(usesLocalCoords)
joshualitt88c23fc2015-05-13 14:18:07 -07001188 , fAAMode(aaMode) {
joshualitteb2a6762014-12-04 11:35:33 -08001189 this->initClassID<DashingLineEffect>();
joshualitt71c92602015-01-14 08:12:47 -08001190 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
joshualitt5224ba72015-02-03 15:07:51 -08001191 fInDashParams = &this->addVertexAttrib(Attribute("inDashParams", kVec3f_GrVertexAttribType));
1192 fInRectParams = &this->addVertexAttrib(Attribute("inRect", kVec4f_GrVertexAttribType));
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001193}
1194
joshualittb0a8a372014-09-23 09:50:21 -07001195GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingLineEffect);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001196
bsalomonc21b09e2015-08-28 18:46:56 -07001197const GrGeometryProcessor* DashingLineEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -07001198 DashAAMode aaMode = static_cast<DashAAMode>(d->fRandom->nextULessThan(kDashAAModeCount));
1199 return DashingLineEffect::Create(GrRandomColor(d->fRandom),
1200 aaMode, GrTest::TestMatrix(d->fRandom),
1201 d->fRandom->nextBool());
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001202}
1203
1204//////////////////////////////////////////////////////////////////////////////
1205
joshualitt5224ba72015-02-03 15:07:51 -08001206static GrGeometryProcessor* create_dash_gp(GrColor color,
senorblancof3c2c462015-04-20 14:44:26 -07001207 DashAAMode dashAAMode,
joshualitt5224ba72015-02-03 15:07:51 -08001208 DashCap cap,
joshualittdf0c5572015-08-03 11:35:28 -07001209 const SkMatrix& viewMatrix,
joshualittb8c241a2015-05-19 08:23:30 -07001210 bool usesLocalCoords) {
joshualittdf0c5572015-08-03 11:35:28 -07001211 SkMatrix invert;
1212 if (usesLocalCoords && !viewMatrix.invert(&invert)) {
1213 SkDebugf("Failed to invert\n");
halcanary96fcdcc2015-08-27 07:41:13 -07001214 return nullptr;
joshualittdf0c5572015-08-03 11:35:28 -07001215 }
1216
egdanielf767e792014-07-02 06:21:32 -07001217 switch (cap) {
joshualitt5224ba72015-02-03 15:07:51 -08001218 case kRound_DashCap:
joshualittdf0c5572015-08-03 11:35:28 -07001219 return DashingCircleEffect::Create(color, dashAAMode, invert, usesLocalCoords);
joshualitt5224ba72015-02-03 15:07:51 -08001220 case kNonRound_DashCap:
joshualittdf0c5572015-08-03 11:35:28 -07001221 return DashingLineEffect::Create(color, dashAAMode, invert, usesLocalCoords);
egdanielf767e792014-07-02 06:21:32 -07001222 }
halcanary96fcdcc2015-08-27 07:41:13 -07001223 return nullptr;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001224}
joshualittfa2008f2015-04-29 11:32:05 -07001225
1226/////////////////////////////////////////////////////////////////////////////////////////////////
1227
1228#ifdef GR_TEST_UTILS
1229
bsalomonabd30f52015-08-13 13:34:48 -07001230DRAW_BATCH_TEST_DEFINE(DashBatch) {
joshualittfa2008f2015-04-29 11:32:05 -07001231 GrColor color = GrRandomColor(random);
1232 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
1233 bool useAA = random->nextBool();
1234 bool msaaRT = random->nextBool();
1235
1236 // We can only dash either horizontal or vertical lines
1237 SkPoint pts[2];
1238 if (random->nextBool()) {
1239 // vertical
1240 pts[0].fX = 1.f;
1241 pts[0].fY = random->nextF() * 10.f;
1242 pts[1].fX = 1.f;
1243 pts[1].fY = random->nextF() * 10.f;
1244 } else {
1245 // horizontal
1246 pts[0].fX = random->nextF() * 10.f;
1247 pts[0].fY = 1.f;
1248 pts[1].fX = random->nextF() * 10.f;
1249 pts[1].fY = 1.f;
1250 }
1251
1252 // pick random cap
1253 SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::Cap::kCapCount));
1254
1255 SkScalar intervals[2];
1256
1257 // We can only dash with the following intervals
1258 enum Intervals {
1259 kOpenOpen_Intervals ,
1260 kOpenClose_Intervals,
1261 kCloseOpen_Intervals,
1262 };
1263
1264 Intervals intervalType = SkPaint::kRound_Cap ?
1265 kOpenClose_Intervals :
1266 Intervals(random->nextULessThan(kCloseOpen_Intervals + 1));
1267 static const SkScalar kIntervalMin = 0.1f;
1268 static const SkScalar kIntervalMax = 10.f;
1269 switch (intervalType) {
1270 case kOpenOpen_Intervals:
1271 intervals[0] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
1272 intervals[1] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
1273 break;
1274 case kOpenClose_Intervals:
1275 intervals[0] = 0.f;
1276 intervals[1] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
1277 break;
1278 case kCloseOpen_Intervals:
1279 intervals[0] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
1280 intervals[1] = 0.f;
1281 break;
1282
1283 }
1284
1285 // phase is 0 < sum (i0, i1)
1286 SkScalar phase = random->nextRangeScalar(0, intervals[0] + intervals[1]);
1287
1288 SkPaint p;
1289 p.setStyle(SkPaint::kStroke_Style);
1290 p.setStrokeWidth(SkIntToScalar(1));
1291 p.setStrokeCap(cap);
1292
1293 GrStrokeInfo strokeInfo(p);
1294
1295 SkPathEffect::DashInfo info;
1296 info.fIntervals = intervals;
1297 info.fCount = 2;
1298 info.fPhase = phase;
1299 SkDEBUGCODE(bool success = ) strokeInfo.setDashInfo(info);
1300 SkASSERT(success);
1301
1302 return create_batch(color, viewMatrix, pts, useAA, strokeInfo, msaaRT);
1303}
1304
1305#endif