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