GrDefaultPathRenderer.cpp revision 99c7c07e0f1f7b78980eb21d84bebda8b45a7178
1/* 2 * Copyright 2011 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 "GrDefaultPathRenderer.h" 9 10#include "GrBatch.h" 11#include "GrBatchTarget.h" 12#include "GrBufferAllocPool.h" 13#include "GrContext.h" 14#include "GrDefaultGeoProcFactory.h" 15#include "GrPathUtils.h" 16#include "GrPipelineBuilder.h" 17#include "GrVertexBuffer.h" 18#include "SkGeometry.h" 19#include "SkString.h" 20#include "SkStrokeRec.h" 21#include "SkTLazy.h" 22#include "SkTraceEvent.h" 23 24GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport, 25 bool stencilWrapOpsSupport) 26 : fSeparateStencil(separateStencilSupport) 27 , fStencilWrapOps(stencilWrapOpsSupport) { 28} 29 30 31//////////////////////////////////////////////////////////////////////////////// 32// Stencil rules for paths 33 34////// Even/Odd 35 36GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass, 37 kInvert_StencilOp, 38 kKeep_StencilOp, 39 kAlwaysIfInClip_StencilFunc, 40 0xffff, 41 0xffff, 42 0xffff); 43 44// ok not to check clip b/c stencil pass only wrote inside clip 45GR_STATIC_CONST_SAME_STENCIL(gEOColorPass, 46 kZero_StencilOp, 47 kZero_StencilOp, 48 kNotEqual_StencilFunc, 49 0xffff, 50 0x0000, 51 0xffff); 52 53// have to check clip b/c outside clip will always be zero. 54GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass, 55 kZero_StencilOp, 56 kZero_StencilOp, 57 kEqualIfInClip_StencilFunc, 58 0xffff, 59 0x0000, 60 0xffff); 61 62////// Winding 63 64// when we have separate stencil we increment front faces / decrement back faces 65// when we don't have wrap incr and decr we use the stencil test to simulate 66// them. 67 68GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap, 69 kIncWrap_StencilOp, kDecWrap_StencilOp, 70 kKeep_StencilOp, kKeep_StencilOp, 71 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, 72 0xffff, 0xffff, 73 0xffff, 0xffff, 74 0xffff, 0xffff); 75 76// if inc'ing the max value, invert to make 0 77// if dec'ing zero invert to make all ones. 78// we can't avoid touching the stencil on both passing and 79// failing, so we can't resctrict ourselves to the clip. 80GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap, 81 kInvert_StencilOp, kInvert_StencilOp, 82 kIncClamp_StencilOp, kDecClamp_StencilOp, 83 kEqual_StencilFunc, kEqual_StencilFunc, 84 0xffff, 0xffff, 85 0xffff, 0x0000, 86 0xffff, 0xffff); 87 88// When there are no separate faces we do two passes to setup the winding rule 89// stencil. First we draw the front faces and inc, then we draw the back faces 90// and dec. These are same as the above two split into the incrementing and 91// decrementing passes. 92GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc, 93 kIncWrap_StencilOp, 94 kKeep_StencilOp, 95 kAlwaysIfInClip_StencilFunc, 96 0xffff, 97 0xffff, 98 0xffff); 99 100GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec, 101 kDecWrap_StencilOp, 102 kKeep_StencilOp, 103 kAlwaysIfInClip_StencilFunc, 104 0xffff, 105 0xffff, 106 0xffff); 107 108GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc, 109 kInvert_StencilOp, 110 kIncClamp_StencilOp, 111 kEqual_StencilFunc, 112 0xffff, 113 0xffff, 114 0xffff); 115 116GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec, 117 kInvert_StencilOp, 118 kDecClamp_StencilOp, 119 kEqual_StencilFunc, 120 0xffff, 121 0x0000, 122 0xffff); 123 124// Color passes are the same whether we use the two-sided stencil or two passes 125 126GR_STATIC_CONST_SAME_STENCIL(gWindColorPass, 127 kZero_StencilOp, 128 kZero_StencilOp, 129 kNonZeroIfInClip_StencilFunc, 130 0xffff, 131 0x0000, 132 0xffff); 133 134GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass, 135 kZero_StencilOp, 136 kZero_StencilOp, 137 kEqualIfInClip_StencilFunc, 138 0xffff, 139 0x0000, 140 0xffff); 141 142////// Normal render to stencil 143 144// Sometimes the default path renderer can draw a path directly to the stencil 145// buffer without having to first resolve the interior / exterior. 146GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil, 147 kZero_StencilOp, 148 kIncClamp_StencilOp, 149 kAlwaysIfInClip_StencilFunc, 150 0xffff, 151 0x0000, 152 0xffff); 153 154//////////////////////////////////////////////////////////////////////////////// 155// Helpers for drawPath 156 157#define STENCIL_OFF 0 // Always disable stencil (even when needed) 158 159static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) { 160#if STENCIL_OFF 161 return true; 162#else 163 if (!stroke.isHairlineStyle() && !path.isInverseFillType()) { 164 return path.isConvex(); 165 } 166 return false; 167#endif 168} 169 170GrPathRenderer::StencilSupport 171GrDefaultPathRenderer::onGetStencilSupport(const GrDrawTarget*, 172 const GrPipelineBuilder*, 173 const SkPath& path, 174 const GrStrokeInfo& stroke) const { 175 if (single_pass_path(path, stroke.getStrokeRec())) { 176 return GrPathRenderer::kNoRestriction_StencilSupport; 177 } else { 178 return GrPathRenderer::kStencilOnly_StencilSupport; 179 } 180} 181 182static inline void append_countour_edge_indices(bool hairLine, 183 uint16_t fanCenterIdx, 184 uint16_t edgeV0Idx, 185 uint16_t** indices) { 186 // when drawing lines we're appending line segments along 187 // the contour. When applying the other fill rules we're 188 // drawing triangle fans around fanCenterIdx. 189 if (!hairLine) { 190 *((*indices)++) = fanCenterIdx; 191 } 192 *((*indices)++) = edgeV0Idx; 193 *((*indices)++) = edgeV0Idx + 1; 194} 195 196static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[], 197 SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed, 198 bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) { 199 // first pt of quad is the pt we ended on in previous step 200 uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset; 201 uint16_t numPts = (uint16_t) 202 GrPathUtils::generateQuadraticPoints( 203 pts[0], pts[1], pts[2], 204 srcSpaceTolSqd, vert, 205 GrPathUtils::quadraticPointCount(pts, srcSpaceTol)); 206 if (indexed) { 207 for (uint16_t i = 0; i < numPts; ++i) { 208 append_countour_edge_indices(isHairline, subpathIdxStart, 209 firstQPtIdx + i, idx); 210 } 211 } 212} 213 214class DefaultPathBatch : public GrBatch { 215public: 216 struct Geometry { 217 GrColor fColor; 218 SkPath fPath; 219 SkScalar fTolerance; 220 }; 221 222 static GrBatch* Create(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix, 223 bool isHairline, const SkRect& devBounds) { 224 return SkNEW_ARGS(DefaultPathBatch, (geometry, coverage, viewMatrix, isHairline, 225 devBounds)); 226 } 227 228 const char* name() const override { return "DefaultPathBatch"; } 229 230 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { 231 // When this is called on a batch, there is only one geometry bundle 232 out->setKnownFourComponents(fGeoData[0].fColor); 233 } 234 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 235 out->setKnownSingleComponent(this->coverage()); 236 } 237 238 void initBatchTracker(const GrPipelineInfo& init) override { 239 // Handle any color overrides 240 if (init.fColorIgnored) { 241 fGeoData[0].fColor = GrColor_ILLEGAL; 242 } else if (GrColor_ILLEGAL != init.fOverrideColor) { 243 fGeoData[0].fColor = init.fOverrideColor; 244 } 245 246 // setup batch properties 247 fBatch.fColorIgnored = init.fColorIgnored; 248 fBatch.fColor = fGeoData[0].fColor; 249 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; 250 fBatch.fCoverageIgnored = init.fCoverageIgnored; 251 } 252 253 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { 254 SkAutoTUnref<const GrGeometryProcessor> gp( 255 GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType, 256 this->color(), 257 this->viewMatrix(), 258 SkMatrix::I(), 259 false, 260 this->coverage())); 261 262 size_t vertexStride = gp->getVertexStride(); 263 SkASSERT(vertexStride == sizeof(SkPoint)); 264 265 batchTarget->initDraw(gp, pipeline); 266 267 // TODO this is hacky, but the only way we have to initialize the GP is to use the 268 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch 269 // everywhere we can remove this nastiness 270 GrPipelineInfo init; 271 init.fColorIgnored = fBatch.fColorIgnored; 272 init.fOverrideColor = GrColor_ILLEGAL; 273 init.fCoverageIgnored = fBatch.fCoverageIgnored; 274 init.fUsesLocalCoords = this->usesLocalCoords(); 275 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); 276 277 int instanceCount = fGeoData.count(); 278 279 // compute number of vertices 280 int maxVertices = 0; 281 282 // We will use index buffers if we have multiple paths or one path with multiple contours 283 bool isIndexed = instanceCount > 1; 284 for (int i = 0; i < instanceCount; i++) { 285 Geometry& args = fGeoData[i]; 286 287 int contourCount; 288 maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount, 289 args.fTolerance); 290 291 isIndexed = isIndexed || contourCount > 1; 292 } 293 294 if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) { 295 SkDebugf("Cannot render path (%d)\n", maxVertices); 296 return; 297 } 298 299 // determine primitiveType 300 int maxIndices = 0; 301 GrPrimitiveType primitiveType; 302 if (this->isHairline()) { 303 if (isIndexed) { 304 maxIndices = 2 * maxVertices; 305 primitiveType = kLines_GrPrimitiveType; 306 } else { 307 primitiveType = kLineStrip_GrPrimitiveType; 308 } 309 } else { 310 if (isIndexed) { 311 maxIndices = 3 * maxVertices; 312 primitiveType = kTriangles_GrPrimitiveType; 313 } else { 314 primitiveType = kTriangleFan_GrPrimitiveType; 315 } 316 } 317 318 // allocate vertex / index buffers 319 const GrVertexBuffer* vertexBuffer; 320 int firstVertex; 321 322 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, 323 maxVertices, 324 &vertexBuffer, 325 &firstVertex); 326 327 if (!vertices) { 328 SkDebugf("Could not allocate vertices\n"); 329 return; 330 } 331 332 const GrIndexBuffer* indexBuffer; 333 int firstIndex; 334 335 void* indices = NULL; 336 if (isIndexed) { 337 indices = batchTarget->indexPool()->makeSpace(maxIndices, 338 &indexBuffer, 339 &firstIndex); 340 341 if (!indices) { 342 SkDebugf("Could not allocate indices\n"); 343 return; 344 } 345 } 346 347 // fill buffers 348 int vertexOffset = 0; 349 int indexOffset = 0; 350 for (int i = 0; i < instanceCount; i++) { 351 Geometry& args = fGeoData[i]; 352 353 int vertexCnt = 0; 354 int indexCnt = 0; 355 if (!this->createGeom(vertices, 356 vertexOffset, 357 indices, 358 indexOffset, 359 &vertexCnt, 360 &indexCnt, 361 args.fPath, 362 args.fTolerance, 363 isIndexed)) { 364 return; 365 } 366 367 vertexOffset += vertexCnt; 368 indexOffset += indexCnt; 369 SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices); 370 } 371 372 GrDrawTarget::DrawInfo drawInfo; 373 drawInfo.setPrimitiveType(primitiveType); 374 drawInfo.setVertexBuffer(vertexBuffer); 375 drawInfo.setStartVertex(firstVertex); 376 drawInfo.setVertexCount(vertexOffset); 377 if (isIndexed) { 378 drawInfo.setIndexBuffer(indexBuffer); 379 drawInfo.setStartIndex(firstIndex); 380 drawInfo.setIndexCount(indexOffset); 381 } else { 382 drawInfo.setStartIndex(0); 383 drawInfo.setIndexCount(0); 384 } 385 batchTarget->draw(drawInfo); 386 387 // put back reserves 388 batchTarget->putBackIndices((size_t)(maxIndices - indexOffset)); 389 batchTarget->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride); 390 } 391 392 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 393 394private: 395 DefaultPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix, 396 bool isHairline, const SkRect& devBounds) { 397 this->initClassID<DefaultPathBatch>(); 398 fBatch.fCoverage = coverage; 399 fBatch.fIsHairline = isHairline; 400 fBatch.fViewMatrix = viewMatrix; 401 fGeoData.push_back(geometry); 402 403 this->setBounds(devBounds); 404 } 405 406 bool onCombineIfPossible(GrBatch* t) override { 407 DefaultPathBatch* that = t->cast<DefaultPathBatch>(); 408 409 if (this->color() != that->color()) { 410 return false; 411 } 412 413 if (this->coverage() != that->coverage()) { 414 return false; 415 } 416 417 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 418 return false; 419 } 420 421 if (this->isHairline() != that->isHairline()) { 422 return false; 423 } 424 425 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 426 this->joinBounds(that->bounds()); 427 return true; 428 } 429 430 bool createGeom(void* vertices, 431 size_t vertexOffset, 432 void* indices, 433 size_t indexOffset, 434 int* vertexCnt, 435 int* indexCnt, 436 const SkPath& path, 437 SkScalar srcSpaceTol, 438 bool isIndexed) { 439 { 440 SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol); 441 442 uint16_t indexOffsetU16 = (uint16_t)indexOffset; 443 uint16_t vertexOffsetU16 = (uint16_t)vertexOffset; 444 445 uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16; 446 uint16_t* idx = idxBase; 447 uint16_t subpathIdxStart = vertexOffsetU16; 448 449 SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset; 450 SkPoint* vert = base; 451 452 SkPoint pts[4]; 453 454 bool first = true; 455 int subpath = 0; 456 457 SkPath::Iter iter(path, false); 458 459 bool done = false; 460 while (!done) { 461 SkPath::Verb verb = iter.next(pts); 462 switch (verb) { 463 case SkPath::kMove_Verb: 464 if (!first) { 465 uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16; 466 subpathIdxStart = currIdx; 467 ++subpath; 468 } 469 *vert = pts[0]; 470 vert++; 471 break; 472 case SkPath::kLine_Verb: 473 if (isIndexed) { 474 uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16; 475 append_countour_edge_indices(this->isHairline(), subpathIdxStart, 476 prevIdx, &idx); 477 } 478 *(vert++) = pts[1]; 479 break; 480 case SkPath::kConic_Verb: { 481 SkScalar weight = iter.conicWeight(); 482 SkAutoConicToQuads converter; 483 // Converting in src-space, hance the finer tolerance (0.25) 484 // TODO: find a way to do this in dev-space so the tolerance means something 485 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f); 486 for (int i = 0; i < converter.countQuads(); ++i) { 487 add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol, 488 isIndexed, this->isHairline(), subpathIdxStart, 489 (int)vertexOffset, &idx); 490 } 491 break; 492 } 493 case SkPath::kQuad_Verb: 494 add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed, 495 this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx); 496 break; 497 case SkPath::kCubic_Verb: { 498 // first pt of cubic is the pt we ended on in previous step 499 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16; 500 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( 501 pts[0], pts[1], pts[2], pts[3], 502 srcSpaceTolSqd, &vert, 503 GrPathUtils::cubicPointCount(pts, srcSpaceTol)); 504 if (isIndexed) { 505 for (uint16_t i = 0; i < numPts; ++i) { 506 append_countour_edge_indices(this->isHairline(), subpathIdxStart, 507 firstCPtIdx + i, &idx); 508 } 509 } 510 break; 511 } 512 case SkPath::kClose_Verb: 513 break; 514 case SkPath::kDone_Verb: 515 done = true; 516 } 517 first = false; 518 } 519 520 *vertexCnt = static_cast<int>(vert - base); 521 *indexCnt = static_cast<int>(idx - idxBase); 522 523 } 524 return true; 525 } 526 527 GrColor color() const { return fBatch.fColor; } 528 uint8_t coverage() const { return fBatch.fCoverage; } 529 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 530 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } 531 bool isHairline() const { return fBatch.fIsHairline; } 532 533 struct BatchTracker { 534 GrColor fColor; 535 uint8_t fCoverage; 536 SkMatrix fViewMatrix; 537 bool fUsesLocalCoords; 538 bool fColorIgnored; 539 bool fCoverageIgnored; 540 bool fIsHairline; 541 }; 542 543 BatchTracker fBatch; 544 SkSTArray<1, Geometry, true> fGeoData; 545}; 546 547bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target, 548 GrPipelineBuilder* pipelineBuilder, 549 GrColor color, 550 const SkMatrix& viewMatrix, 551 const SkPath& path, 552 const GrStrokeInfo& origStroke, 553 bool stencilOnly) { 554 SkTCopyOnFirstWrite<GrStrokeInfo> stroke(origStroke); 555 556 SkScalar hairlineCoverage; 557 uint8_t newCoverage = 0xff; 558 if (IsStrokeHairlineOrEquivalent(*stroke, viewMatrix, &hairlineCoverage)) { 559 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff); 560 561 if (!stroke->getStrokeRec().isHairlineStyle()) { 562 stroke.writable()->getStrokeRecPtr()->setHairlineStyle(); 563 } 564 } 565 566 const bool isHairline = stroke->getStrokeRec().isHairlineStyle(); 567 568 // Save the current xp on the draw state so we can reset it if needed 569 SkAutoTUnref<const GrXPFactory> backupXPFactory(SkRef(pipelineBuilder->getXPFactory())); 570 // face culling doesn't make sense here 571 SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace()); 572 573 int passCount = 0; 574 const GrStencilSettings* passes[3]; 575 GrPipelineBuilder::DrawFace drawFace[3]; 576 bool reverse = false; 577 bool lastPassIsBounds; 578 579 if (isHairline) { 580 passCount = 1; 581 if (stencilOnly) { 582 passes[0] = &gDirectToStencil; 583 } else { 584 passes[0] = NULL; 585 } 586 lastPassIsBounds = false; 587 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace; 588 } else { 589 if (single_pass_path(path, stroke->getStrokeRec())) { 590 passCount = 1; 591 if (stencilOnly) { 592 passes[0] = &gDirectToStencil; 593 } else { 594 passes[0] = NULL; 595 } 596 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace; 597 lastPassIsBounds = false; 598 } else { 599 switch (path.getFillType()) { 600 case SkPath::kInverseEvenOdd_FillType: 601 reverse = true; 602 // fallthrough 603 case SkPath::kEvenOdd_FillType: 604 passes[0] = &gEOStencilPass; 605 if (stencilOnly) { 606 passCount = 1; 607 lastPassIsBounds = false; 608 } else { 609 passCount = 2; 610 lastPassIsBounds = true; 611 if (reverse) { 612 passes[1] = &gInvEOColorPass; 613 } else { 614 passes[1] = &gEOColorPass; 615 } 616 } 617 drawFace[0] = drawFace[1] = GrPipelineBuilder::kBoth_DrawFace; 618 break; 619 620 case SkPath::kInverseWinding_FillType: 621 reverse = true; 622 // fallthrough 623 case SkPath::kWinding_FillType: 624 if (fSeparateStencil) { 625 if (fStencilWrapOps) { 626 passes[0] = &gWindStencilSeparateWithWrap; 627 } else { 628 passes[0] = &gWindStencilSeparateNoWrap; 629 } 630 passCount = 2; 631 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace; 632 } else { 633 if (fStencilWrapOps) { 634 passes[0] = &gWindSingleStencilWithWrapInc; 635 passes[1] = &gWindSingleStencilWithWrapDec; 636 } else { 637 passes[0] = &gWindSingleStencilNoWrapInc; 638 passes[1] = &gWindSingleStencilNoWrapDec; 639 } 640 // which is cw and which is ccw is arbitrary. 641 drawFace[0] = GrPipelineBuilder::kCW_DrawFace; 642 drawFace[1] = GrPipelineBuilder::kCCW_DrawFace; 643 passCount = 3; 644 } 645 if (stencilOnly) { 646 lastPassIsBounds = false; 647 --passCount; 648 } else { 649 lastPassIsBounds = true; 650 drawFace[passCount-1] = GrPipelineBuilder::kBoth_DrawFace; 651 if (reverse) { 652 passes[passCount-1] = &gInvWindColorPass; 653 } else { 654 passes[passCount-1] = &gWindColorPass; 655 } 656 } 657 break; 658 default: 659 SkDEBUGFAIL("Unknown path fFill!"); 660 return false; 661 } 662 } 663 } 664 665 SkScalar tol = GrPathUtils::kDefaultTolerance; 666 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds()); 667 668 SkRect devBounds; 669 GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds); 670 671 for (int p = 0; p < passCount; ++p) { 672 pipelineBuilder->setDrawFace(drawFace[p]); 673 if (passes[p]) { 674 *pipelineBuilder->stencil() = *passes[p]; 675 } 676 677 if (lastPassIsBounds && (p == passCount-1)) { 678 // Reset the XP Factory on pipelineBuilder 679 pipelineBuilder->setXPFactory(backupXPFactory); 680 SkRect bounds; 681 SkMatrix localMatrix = SkMatrix::I(); 682 if (reverse) { 683 SkASSERT(pipelineBuilder->getRenderTarget()); 684 // draw over the dev bounds (which will be the whole dst surface for inv fill). 685 bounds = devBounds; 686 SkMatrix vmi; 687 // mapRect through persp matrix may not be correct 688 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) { 689 vmi.mapRect(&bounds); 690 } else { 691 if (!viewMatrix.invert(&localMatrix)) { 692 return false; 693 } 694 } 695 } else { 696 bounds = path.getBounds(); 697 } 698 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() : 699 viewMatrix; 700 target->drawRect(pipelineBuilder, color, viewM, bounds, NULL, &localMatrix); 701 } else { 702 if (passCount > 1) { 703 pipelineBuilder->setDisableColorXPFactory(); 704 } 705 706 DefaultPathBatch::Geometry geometry; 707 geometry.fColor = color; 708 geometry.fPath = path; 709 geometry.fTolerance = srcSpaceTol; 710 711 SkAutoTUnref<GrBatch> batch(DefaultPathBatch::Create(geometry, newCoverage, viewMatrix, 712 isHairline, devBounds)); 713 714 target->drawBatch(pipelineBuilder, batch); 715 } 716 } 717 return true; 718} 719 720bool GrDefaultPathRenderer::canDrawPath(const GrDrawTarget* target, 721 const GrPipelineBuilder* pipelineBuilder, 722 const SkMatrix& viewMatrix, 723 const SkPath& path, 724 const GrStrokeInfo& stroke, 725 bool antiAlias) const { 726 // this class can draw any path with any fill but doesn't do any anti-aliasing. 727 return !antiAlias && (stroke.isFillStyle() || IsStrokeHairlineOrEquivalent(stroke, 728 viewMatrix, 729 NULL)); 730} 731 732bool GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target, 733 GrPipelineBuilder* pipelineBuilder, 734 GrColor color, 735 const SkMatrix& viewMatrix, 736 const SkPath& path, 737 const GrStrokeInfo& stroke, 738 bool antiAlias) { 739 return this->internalDrawPath(target, 740 pipelineBuilder, 741 color, 742 viewMatrix, 743 path, 744 stroke, 745 false); 746} 747 748void GrDefaultPathRenderer::onStencilPath(GrDrawTarget* target, 749 GrPipelineBuilder* pipelineBuilder, 750 const SkMatrix& viewMatrix, 751 const SkPath& path, 752 const GrStrokeInfo& stroke) { 753 SkASSERT(SkPath::kInverseEvenOdd_FillType != path.getFillType()); 754 SkASSERT(SkPath::kInverseWinding_FillType != path.getFillType()); 755 this->internalDrawPath(target, pipelineBuilder, GrColor_WHITE, viewMatrix, path, stroke, true); 756} 757