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