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