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