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 "GrBatchTest.h" 13#include "GrContext.h" 14#include "GrDefaultGeoProcFactory.h" 15#include "GrPathUtils.h" 16#include "GrPipelineBuilder.h" 17#include "GrVertices.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 this->coverage())); 260 261 size_t vertexStride = gp->getVertexStride(); 262 SkASSERT(vertexStride == sizeof(SkPoint)); 263 264 batchTarget->initDraw(gp, pipeline); 265 266 // TODO this is hacky, but the only way we have to initialize the GP is to use the 267 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch 268 // everywhere we can remove this nastiness 269 GrPipelineInfo init; 270 init.fColorIgnored = fBatch.fColorIgnored; 271 init.fOverrideColor = GrColor_ILLEGAL; 272 init.fCoverageIgnored = fBatch.fCoverageIgnored; 273 init.fUsesLocalCoords = this->usesLocalCoords(); 274 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); 275 276 int instanceCount = fGeoData.count(); 277 278 // compute number of vertices 279 int maxVertices = 0; 280 281 // We will use index buffers if we have multiple paths or one path with multiple contours 282 bool isIndexed = instanceCount > 1; 283 for (int i = 0; i < instanceCount; i++) { 284 Geometry& args = fGeoData[i]; 285 286 int contourCount; 287 maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount, 288 args.fTolerance); 289 290 isIndexed = isIndexed || contourCount > 1; 291 } 292 293 if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) { 294 SkDebugf("Cannot render path (%d)\n", maxVertices); 295 return; 296 } 297 298 // determine primitiveType 299 int maxIndices = 0; 300 GrPrimitiveType primitiveType; 301 if (this->isHairline()) { 302 if (isIndexed) { 303 maxIndices = 2 * maxVertices; 304 primitiveType = kLines_GrPrimitiveType; 305 } else { 306 primitiveType = kLineStrip_GrPrimitiveType; 307 } 308 } else { 309 if (isIndexed) { 310 maxIndices = 3 * maxVertices; 311 primitiveType = kTriangles_GrPrimitiveType; 312 } else { 313 primitiveType = kTriangleFan_GrPrimitiveType; 314 } 315 } 316 317 // allocate vertex / index buffers 318 const GrVertexBuffer* vertexBuffer; 319 int firstVertex; 320 321 void* verts = batchTarget->makeVertSpace(vertexStride, maxVertices, 322 &vertexBuffer, &firstVertex); 323 324 if (!verts) { 325 SkDebugf("Could not allocate vertices\n"); 326 return; 327 } 328 329 const GrIndexBuffer* indexBuffer = NULL; 330 int firstIndex = 0; 331 332 void* indices = NULL; 333 if (isIndexed) { 334 indices = batchTarget->makeIndexSpace(maxIndices, &indexBuffer, &firstIndex); 335 336 if (!indices) { 337 SkDebugf("Could not allocate indices\n"); 338 return; 339 } 340 } 341 342 // fill buffers 343 int vertexOffset = 0; 344 int indexOffset = 0; 345 for (int i = 0; i < instanceCount; i++) { 346 Geometry& args = fGeoData[i]; 347 348 int vertexCnt = 0; 349 int indexCnt = 0; 350 if (!this->createGeom(verts, 351 vertexOffset, 352 indices, 353 indexOffset, 354 &vertexCnt, 355 &indexCnt, 356 args.fPath, 357 args.fTolerance, 358 isIndexed)) { 359 return; 360 } 361 362 vertexOffset += vertexCnt; 363 indexOffset += indexCnt; 364 SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices); 365 } 366 367 GrVertices vertices; 368 if (isIndexed) { 369 vertices.initIndexed(primitiveType, vertexBuffer, indexBuffer, firstVertex, firstIndex, 370 vertexOffset, indexOffset); 371 } else { 372 vertices.init(primitiveType, vertexBuffer, firstVertex, vertexOffset); 373 } 374 batchTarget->draw(vertices); 375 376 // put back reserves 377 batchTarget->putBackIndices((size_t)(maxIndices - indexOffset)); 378 batchTarget->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride); 379 } 380 381 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 382 383private: 384 DefaultPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix, 385 bool isHairline, const SkRect& devBounds) { 386 this->initClassID<DefaultPathBatch>(); 387 fBatch.fCoverage = coverage; 388 fBatch.fIsHairline = isHairline; 389 fBatch.fViewMatrix = viewMatrix; 390 fGeoData.push_back(geometry); 391 392 this->setBounds(devBounds); 393 } 394 395 bool onCombineIfPossible(GrBatch* t) override { 396 DefaultPathBatch* that = t->cast<DefaultPathBatch>(); 397 398 if (this->color() != that->color()) { 399 return false; 400 } 401 402 if (this->coverage() != that->coverage()) { 403 return false; 404 } 405 406 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 407 return false; 408 } 409 410 if (this->isHairline() != that->isHairline()) { 411 return false; 412 } 413 414 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 415 this->joinBounds(that->bounds()); 416 return true; 417 } 418 419 bool createGeom(void* vertices, 420 size_t vertexOffset, 421 void* indices, 422 size_t indexOffset, 423 int* vertexCnt, 424 int* indexCnt, 425 const SkPath& path, 426 SkScalar srcSpaceTol, 427 bool isIndexed) { 428 { 429 SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol); 430 431 uint16_t indexOffsetU16 = (uint16_t)indexOffset; 432 uint16_t vertexOffsetU16 = (uint16_t)vertexOffset; 433 434 uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16; 435 uint16_t* idx = idxBase; 436 uint16_t subpathIdxStart = vertexOffsetU16; 437 438 SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset; 439 SkPoint* vert = base; 440 441 SkPoint pts[4]; 442 443 bool first = true; 444 int subpath = 0; 445 446 SkPath::Iter iter(path, false); 447 448 bool done = false; 449 while (!done) { 450 SkPath::Verb verb = iter.next(pts); 451 switch (verb) { 452 case SkPath::kMove_Verb: 453 if (!first) { 454 uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16; 455 subpathIdxStart = currIdx; 456 ++subpath; 457 } 458 *vert = pts[0]; 459 vert++; 460 break; 461 case SkPath::kLine_Verb: 462 if (isIndexed) { 463 uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16; 464 append_countour_edge_indices(this->isHairline(), subpathIdxStart, 465 prevIdx, &idx); 466 } 467 *(vert++) = pts[1]; 468 break; 469 case SkPath::kConic_Verb: { 470 SkScalar weight = iter.conicWeight(); 471 SkAutoConicToQuads converter; 472 // Converting in src-space, hance the finer tolerance (0.25) 473 // TODO: find a way to do this in dev-space so the tolerance means something 474 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f); 475 for (int i = 0; i < converter.countQuads(); ++i) { 476 add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol, 477 isIndexed, this->isHairline(), subpathIdxStart, 478 (int)vertexOffset, &idx); 479 } 480 break; 481 } 482 case SkPath::kQuad_Verb: 483 add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed, 484 this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx); 485 break; 486 case SkPath::kCubic_Verb: { 487 // first pt of cubic is the pt we ended on in previous step 488 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16; 489 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( 490 pts[0], pts[1], pts[2], pts[3], 491 srcSpaceTolSqd, &vert, 492 GrPathUtils::cubicPointCount(pts, srcSpaceTol)); 493 if (isIndexed) { 494 for (uint16_t i = 0; i < numPts; ++i) { 495 append_countour_edge_indices(this->isHairline(), subpathIdxStart, 496 firstCPtIdx + i, &idx); 497 } 498 } 499 break; 500 } 501 case SkPath::kClose_Verb: 502 break; 503 case SkPath::kDone_Verb: 504 done = true; 505 } 506 first = false; 507 } 508 509 *vertexCnt = static_cast<int>(vert - base); 510 *indexCnt = static_cast<int>(idx - idxBase); 511 512 } 513 return true; 514 } 515 516 GrColor color() const { return fBatch.fColor; } 517 uint8_t coverage() const { return fBatch.fCoverage; } 518 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 519 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } 520 bool isHairline() const { return fBatch.fIsHairline; } 521 522 struct BatchTracker { 523 GrColor fColor; 524 uint8_t fCoverage; 525 SkMatrix fViewMatrix; 526 bool fUsesLocalCoords; 527 bool fColorIgnored; 528 bool fCoverageIgnored; 529 bool fIsHairline; 530 }; 531 532 BatchTracker fBatch; 533 SkSTArray<1, Geometry, true> fGeoData; 534}; 535 536bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target, 537 GrPipelineBuilder* pipelineBuilder, 538 GrColor color, 539 const SkMatrix& viewMatrix, 540 const SkPath& path, 541 const GrStrokeInfo& origStroke, 542 bool stencilOnly) { 543 SkTCopyOnFirstWrite<GrStrokeInfo> stroke(origStroke); 544 545 SkScalar hairlineCoverage; 546 uint8_t newCoverage = 0xff; 547 if (IsStrokeHairlineOrEquivalent(*stroke, viewMatrix, &hairlineCoverage)) { 548 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff); 549 550 if (!stroke->getStrokeRec().isHairlineStyle()) { 551 stroke.writable()->getStrokeRecPtr()->setHairlineStyle(); 552 } 553 } 554 555 const bool isHairline = stroke->getStrokeRec().isHairlineStyle(); 556 557 // Save the current xp on the draw state so we can reset it if needed 558 SkAutoTUnref<const GrXPFactory> backupXPFactory(SkRef(pipelineBuilder->getXPFactory())); 559 // face culling doesn't make sense here 560 SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace()); 561 562 int passCount = 0; 563 const GrStencilSettings* passes[3]; 564 GrPipelineBuilder::DrawFace drawFace[3]; 565 bool reverse = false; 566 bool lastPassIsBounds; 567 568 if (isHairline) { 569 passCount = 1; 570 if (stencilOnly) { 571 passes[0] = &gDirectToStencil; 572 } else { 573 passes[0] = NULL; 574 } 575 lastPassIsBounds = false; 576 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace; 577 } else { 578 if (single_pass_path(path, stroke->getStrokeRec())) { 579 passCount = 1; 580 if (stencilOnly) { 581 passes[0] = &gDirectToStencil; 582 } else { 583 passes[0] = NULL; 584 } 585 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace; 586 lastPassIsBounds = false; 587 } else { 588 switch (path.getFillType()) { 589 case SkPath::kInverseEvenOdd_FillType: 590 reverse = true; 591 // fallthrough 592 case SkPath::kEvenOdd_FillType: 593 passes[0] = &gEOStencilPass; 594 if (stencilOnly) { 595 passCount = 1; 596 lastPassIsBounds = false; 597 } else { 598 passCount = 2; 599 lastPassIsBounds = true; 600 if (reverse) { 601 passes[1] = &gInvEOColorPass; 602 } else { 603 passes[1] = &gEOColorPass; 604 } 605 } 606 drawFace[0] = drawFace[1] = GrPipelineBuilder::kBoth_DrawFace; 607 break; 608 609 case SkPath::kInverseWinding_FillType: 610 reverse = true; 611 // fallthrough 612 case SkPath::kWinding_FillType: 613 if (fSeparateStencil) { 614 if (fStencilWrapOps) { 615 passes[0] = &gWindStencilSeparateWithWrap; 616 } else { 617 passes[0] = &gWindStencilSeparateNoWrap; 618 } 619 passCount = 2; 620 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace; 621 } else { 622 if (fStencilWrapOps) { 623 passes[0] = &gWindSingleStencilWithWrapInc; 624 passes[1] = &gWindSingleStencilWithWrapDec; 625 } else { 626 passes[0] = &gWindSingleStencilNoWrapInc; 627 passes[1] = &gWindSingleStencilNoWrapDec; 628 } 629 // which is cw and which is ccw is arbitrary. 630 drawFace[0] = GrPipelineBuilder::kCW_DrawFace; 631 drawFace[1] = GrPipelineBuilder::kCCW_DrawFace; 632 passCount = 3; 633 } 634 if (stencilOnly) { 635 lastPassIsBounds = false; 636 --passCount; 637 } else { 638 lastPassIsBounds = true; 639 drawFace[passCount-1] = GrPipelineBuilder::kBoth_DrawFace; 640 if (reverse) { 641 passes[passCount-1] = &gInvWindColorPass; 642 } else { 643 passes[passCount-1] = &gWindColorPass; 644 } 645 } 646 break; 647 default: 648 SkDEBUGFAIL("Unknown path fFill!"); 649 return false; 650 } 651 } 652 } 653 654 SkScalar tol = GrPathUtils::kDefaultTolerance; 655 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds()); 656 657 SkRect devBounds; 658 GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds); 659 660 for (int p = 0; p < passCount; ++p) { 661 pipelineBuilder->setDrawFace(drawFace[p]); 662 if (passes[p]) { 663 *pipelineBuilder->stencil() = *passes[p]; 664 } 665 666 if (lastPassIsBounds && (p == passCount-1)) { 667 // Reset the XP Factory on pipelineBuilder 668 pipelineBuilder->setXPFactory(backupXPFactory); 669 SkRect bounds; 670 SkMatrix localMatrix = SkMatrix::I(); 671 if (reverse) { 672 SkASSERT(pipelineBuilder->getRenderTarget()); 673 // draw over the dev bounds (which will be the whole dst surface for inv fill). 674 bounds = devBounds; 675 SkMatrix vmi; 676 // mapRect through persp matrix may not be correct 677 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) { 678 vmi.mapRect(&bounds); 679 } else { 680 if (!viewMatrix.invert(&localMatrix)) { 681 return false; 682 } 683 } 684 } else { 685 bounds = path.getBounds(); 686 } 687 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() : 688 viewMatrix; 689 target->drawRect(pipelineBuilder, color, viewM, bounds, NULL, &localMatrix); 690 } else { 691 if (passCount > 1) { 692 pipelineBuilder->setDisableColorXPFactory(); 693 } 694 695 DefaultPathBatch::Geometry geometry; 696 geometry.fColor = color; 697 geometry.fPath = path; 698 geometry.fTolerance = srcSpaceTol; 699 700 SkAutoTUnref<GrBatch> batch(DefaultPathBatch::Create(geometry, newCoverage, viewMatrix, 701 isHairline, devBounds)); 702 703 target->drawBatch(pipelineBuilder, batch); 704 } 705 } 706 return true; 707} 708 709bool GrDefaultPathRenderer::canDrawPath(const GrDrawTarget* target, 710 const GrPipelineBuilder* pipelineBuilder, 711 const SkMatrix& viewMatrix, 712 const SkPath& path, 713 const GrStrokeInfo& stroke, 714 bool antiAlias) const { 715 // this class can draw any path with any fill but doesn't do any anti-aliasing. 716 return !antiAlias && (stroke.isFillStyle() || IsStrokeHairlineOrEquivalent(stroke, 717 viewMatrix, 718 NULL)); 719} 720 721bool GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target, 722 GrPipelineBuilder* pipelineBuilder, 723 GrColor color, 724 const SkMatrix& viewMatrix, 725 const SkPath& path, 726 const GrStrokeInfo& stroke, 727 bool antiAlias) { 728 return this->internalDrawPath(target, 729 pipelineBuilder, 730 color, 731 viewMatrix, 732 path, 733 stroke, 734 false); 735} 736 737void GrDefaultPathRenderer::onStencilPath(GrDrawTarget* target, 738 GrPipelineBuilder* pipelineBuilder, 739 const SkMatrix& viewMatrix, 740 const SkPath& path, 741 const GrStrokeInfo& stroke) { 742 SkASSERT(SkPath::kInverseEvenOdd_FillType != path.getFillType()); 743 SkASSERT(SkPath::kInverseWinding_FillType != path.getFillType()); 744 this->internalDrawPath(target, pipelineBuilder, GrColor_WHITE, viewMatrix, path, stroke, true); 745} 746 747/////////////////////////////////////////////////////////////////////////////////////////////////// 748 749#ifdef GR_TEST_UTILS 750 751BATCH_TEST_DEFINE(DefaultPathBatch) { 752 GrColor color = GrRandomColor(random); 753 SkMatrix viewMatrix = GrTest::TestMatrix(random); 754 755 // For now just hairlines because the other types of draws require two batches. 756 // TODO we should figure out a way to combine the stencil and cover steps into one batch 757 GrStrokeInfo stroke(SkStrokeRec::kHairline_InitStyle); 758 SkPath path = GrTest::TestPath(random); 759 760 // Compute srcSpaceTol 761 SkRect bounds = path.getBounds(); 762 SkScalar tol = GrPathUtils::kDefaultTolerance; 763 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds); 764 765 DefaultPathBatch::Geometry geometry; 766 geometry.fColor = color; 767 geometry.fPath = path; 768 geometry.fTolerance = srcSpaceTol; 769 770 viewMatrix.mapRect(&bounds); 771 uint8_t coverage = GrRandomCoverage(random); 772 return DefaultPathBatch::Create(geometry, coverage, viewMatrix, true, bounds); 773} 774 775#endif 776