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