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