GrDefaultPathRenderer.cpp revision 7950a9eba71f65365d88021680a16f245ad3fa68
1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9#include "GrDefaultPathRenderer.h" 10 11#include "GrContext.h" 12#include "GrDrawState.h" 13#include "GrPathUtils.h" 14#include "SkString.h" 15#include "SkStrokeRec.h" 16#include "SkTrace.h" 17 18 19GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport, 20 bool stencilWrapOpsSupport) 21 : fSeparateStencil(separateStencilSupport) 22 , fStencilWrapOps(stencilWrapOpsSupport) { 23} 24 25 26//////////////////////////////////////////////////////////////////////////////// 27// Stencil rules for paths 28 29////// Even/Odd 30 31GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass, 32 kInvert_StencilOp, 33 kKeep_StencilOp, 34 kAlwaysIfInClip_StencilFunc, 35 0xffff, 36 0xffff, 37 0xffff); 38 39// ok not to check clip b/c stencil pass only wrote inside clip 40GR_STATIC_CONST_SAME_STENCIL(gEOColorPass, 41 kZero_StencilOp, 42 kZero_StencilOp, 43 kNotEqual_StencilFunc, 44 0xffff, 45 0x0000, 46 0xffff); 47 48// have to check clip b/c outside clip will always be zero. 49GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass, 50 kZero_StencilOp, 51 kZero_StencilOp, 52 kEqualIfInClip_StencilFunc, 53 0xffff, 54 0x0000, 55 0xffff); 56 57////// Winding 58 59// when we have separate stencil we increment front faces / decrement back faces 60// when we don't have wrap incr and decr we use the stencil test to simulate 61// them. 62 63GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap, 64 kIncWrap_StencilOp, kDecWrap_StencilOp, 65 kKeep_StencilOp, kKeep_StencilOp, 66 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, 67 0xffff, 0xffff, 68 0xffff, 0xffff, 69 0xffff, 0xffff); 70 71// if inc'ing the max value, invert to make 0 72// if dec'ing zero invert to make all ones. 73// we can't avoid touching the stencil on both passing and 74// failing, so we can't resctrict ourselves to the clip. 75GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap, 76 kInvert_StencilOp, kInvert_StencilOp, 77 kIncClamp_StencilOp, kDecClamp_StencilOp, 78 kEqual_StencilFunc, kEqual_StencilFunc, 79 0xffff, 0xffff, 80 0xffff, 0x0000, 81 0xffff, 0xffff); 82 83// When there are no separate faces we do two passes to setup the winding rule 84// stencil. First we draw the front faces and inc, then we draw the back faces 85// and dec. These are same as the above two split into the incrementing and 86// decrementing passes. 87GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc, 88 kIncWrap_StencilOp, 89 kKeep_StencilOp, 90 kAlwaysIfInClip_StencilFunc, 91 0xffff, 92 0xffff, 93 0xffff); 94 95GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec, 96 kDecWrap_StencilOp, 97 kKeep_StencilOp, 98 kAlwaysIfInClip_StencilFunc, 99 0xffff, 100 0xffff, 101 0xffff); 102 103GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc, 104 kInvert_StencilOp, 105 kIncClamp_StencilOp, 106 kEqual_StencilFunc, 107 0xffff, 108 0xffff, 109 0xffff); 110 111GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec, 112 kInvert_StencilOp, 113 kDecClamp_StencilOp, 114 kEqual_StencilFunc, 115 0xffff, 116 0x0000, 117 0xffff); 118 119// Color passes are the same whether we use the two-sided stencil or two passes 120 121GR_STATIC_CONST_SAME_STENCIL(gWindColorPass, 122 kZero_StencilOp, 123 kZero_StencilOp, 124 kNonZeroIfInClip_StencilFunc, 125 0xffff, 126 0x0000, 127 0xffff); 128 129GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass, 130 kZero_StencilOp, 131 kZero_StencilOp, 132 kEqualIfInClip_StencilFunc, 133 0xffff, 134 0x0000, 135 0xffff); 136 137////// Normal render to stencil 138 139// Sometimes the default path renderer can draw a path directly to the stencil 140// buffer without having to first resolve the interior / exterior. 141GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil, 142 kZero_StencilOp, 143 kIncClamp_StencilOp, 144 kAlwaysIfInClip_StencilFunc, 145 0xffff, 146 0x0000, 147 0xffff); 148 149//////////////////////////////////////////////////////////////////////////////// 150// Helpers for drawPath 151 152#define STENCIL_OFF 0 // Always disable stencil (even when needed) 153 154static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) { 155#if STENCIL_OFF 156 return true; 157#else 158 if (!stroke.isHairlineStyle() && !path.isInverseFillType()) { 159 return path.isConvex(); 160 } 161 return false; 162#endif 163} 164 165GrPathRenderer::StencilSupport GrDefaultPathRenderer::onGetStencilSupport( 166 const SkPath& path, 167 const SkStrokeRec& stroke, 168 const GrDrawTarget*) const { 169 if (single_pass_path(path, stroke)) { 170 return GrPathRenderer::kNoRestriction_StencilSupport; 171 } else { 172 return GrPathRenderer::kStencilOnly_StencilSupport; 173 } 174} 175 176static inline void append_countour_edge_indices(bool hairLine, 177 uint16_t fanCenterIdx, 178 uint16_t edgeV0Idx, 179 uint16_t** indices) { 180 // when drawing lines we're appending line segments along 181 // the contour. When applying the other fill rules we're 182 // drawing triangle fans around fanCenterIdx. 183 if (!hairLine) { 184 *((*indices)++) = fanCenterIdx; 185 } 186 *((*indices)++) = edgeV0Idx; 187 *((*indices)++) = edgeV0Idx + 1; 188} 189 190bool GrDefaultPathRenderer::createGeom(const SkPath& path, 191 const SkStrokeRec& stroke, 192 SkScalar srcSpaceTol, 193 GrDrawTarget* target, 194 GrPrimitiveType* primType, 195 int* vertexCnt, 196 int* indexCnt, 197 GrDrawTarget::AutoReleaseGeometry* arg) { 198 { 199 SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom"); 200 201 SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol); 202 int contourCnt; 203 int maxPts = GrPathUtils::worstCasePointCount(path, &contourCnt, 204 srcSpaceTol); 205 206 if (maxPts <= 0) { 207 return false; 208 } 209 if (maxPts > ((int)SK_MaxU16 + 1)) { 210 GrPrintf("Path not rendered, too many verts (%d)\n", maxPts); 211 return false; 212 } 213 214 bool indexed = contourCnt > 1; 215 216 const bool isHairline = stroke.isHairlineStyle(); 217 218 int maxIdxs = 0; 219 if (isHairline) { 220 if (indexed) { 221 maxIdxs = 2 * maxPts; 222 *primType = kLines_GrPrimitiveType; 223 } else { 224 *primType = kLineStrip_GrPrimitiveType; 225 } 226 } else { 227 if (indexed) { 228 maxIdxs = 3 * maxPts; 229 *primType = kTriangles_GrPrimitiveType; 230 } else { 231 *primType = kTriangleFan_GrPrimitiveType; 232 } 233 } 234 235 target->drawState()->setDefaultVertexAttribs(); 236 if (!arg->set(target, maxPts, maxIdxs)) { 237 return false; 238 } 239 240 uint16_t* idxBase = reinterpret_cast<uint16_t*>(arg->indices()); 241 uint16_t* idx = idxBase; 242 uint16_t subpathIdxStart = 0; 243 244 GrPoint* base = reinterpret_cast<GrPoint*>(arg->vertices()); 245 GrAssert(NULL != base); 246 GrPoint* vert = base; 247 248 GrPoint pts[4]; 249 250 bool first = true; 251 int subpath = 0; 252 253 SkPath::Iter iter(path, false); 254 255 for (;;) { 256 SkPath::Verb verb = iter.next(pts); 257 switch (verb) { 258 case SkPath::kConic_Verb: 259 SkASSERT(0); 260 break; 261 case SkPath::kMove_Verb: 262 if (!first) { 263 uint16_t currIdx = (uint16_t) (vert - base); 264 subpathIdxStart = currIdx; 265 ++subpath; 266 } 267 *vert = pts[0]; 268 vert++; 269 break; 270 case SkPath::kLine_Verb: 271 if (indexed) { 272 uint16_t prevIdx = (uint16_t)(vert - base) - 1; 273 append_countour_edge_indices(isHairline, subpathIdxStart, 274 prevIdx, &idx); 275 } 276 *(vert++) = pts[1]; 277 break; 278 case SkPath::kQuad_Verb: { 279 // first pt of quad is the pt we ended on in previous step 280 uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1; 281 uint16_t numPts = (uint16_t) 282 GrPathUtils::generateQuadraticPoints( 283 pts[0], pts[1], pts[2], 284 srcSpaceTolSqd, &vert, 285 GrPathUtils::quadraticPointCount(pts, srcSpaceTol)); 286 if (indexed) { 287 for (uint16_t i = 0; i < numPts; ++i) { 288 append_countour_edge_indices(isHairline, subpathIdxStart, 289 firstQPtIdx + i, &idx); 290 } 291 } 292 break; 293 } 294 case SkPath::kCubic_Verb: { 295 // first pt of cubic is the pt we ended on in previous step 296 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1; 297 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( 298 pts[0], pts[1], pts[2], pts[3], 299 srcSpaceTolSqd, &vert, 300 GrPathUtils::cubicPointCount(pts, srcSpaceTol)); 301 if (indexed) { 302 for (uint16_t i = 0; i < numPts; ++i) { 303 append_countour_edge_indices(isHairline, subpathIdxStart, 304 firstCPtIdx + i, &idx); 305 } 306 } 307 break; 308 } 309 case SkPath::kClose_Verb: 310 break; 311 case SkPath::kDone_Verb: 312 // uint16_t currIdx = (uint16_t) (vert - base); 313 goto FINISHED; 314 } 315 first = false; 316 } 317FINISHED: 318 GrAssert((vert - base) <= maxPts); 319 GrAssert((idx - idxBase) <= maxIdxs); 320 321 *vertexCnt = vert - base; 322 *indexCnt = idx - idxBase; 323 324 } 325 return true; 326} 327 328bool GrDefaultPathRenderer::internalDrawPath(const SkPath& path, 329 const SkStrokeRec& stroke, 330 GrDrawTarget* target, 331 bool stencilOnly) { 332 333 SkMatrix viewM = target->getDrawState().getViewMatrix(); 334 SkScalar tol = SK_Scalar1; 335 tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds()); 336 337 int vertexCnt; 338 int indexCnt; 339 GrPrimitiveType primType; 340 GrDrawTarget::AutoReleaseGeometry arg; 341 if (!this->createGeom(path, 342 stroke, 343 tol, 344 target, 345 &primType, 346 &vertexCnt, 347 &indexCnt, 348 &arg)) { 349 return false; 350 } 351 352 GrAssert(NULL != target); 353 GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit); 354 GrDrawState* drawState = target->drawState(); 355 bool colorWritesWereDisabled = drawState->isColorWriteDisabled(); 356 // face culling doesn't make sense here 357 GrAssert(GrDrawState::kBoth_DrawFace == drawState->getDrawFace()); 358 359 int passCount = 0; 360 const GrStencilSettings* passes[3]; 361 GrDrawState::DrawFace drawFace[3]; 362 bool reverse = false; 363 bool lastPassIsBounds; 364 365 if (stroke.isHairlineStyle()) { 366 passCount = 1; 367 if (stencilOnly) { 368 passes[0] = &gDirectToStencil; 369 } else { 370 passes[0] = NULL; 371 } 372 lastPassIsBounds = false; 373 drawFace[0] = GrDrawState::kBoth_DrawFace; 374 } else { 375 if (single_pass_path(path, stroke)) { 376 passCount = 1; 377 if (stencilOnly) { 378 passes[0] = &gDirectToStencil; 379 } else { 380 passes[0] = NULL; 381 } 382 drawFace[0] = GrDrawState::kBoth_DrawFace; 383 lastPassIsBounds = false; 384 } else { 385 switch (path.getFillType()) { 386 case SkPath::kInverseEvenOdd_FillType: 387 reverse = true; 388 // fallthrough 389 case SkPath::kEvenOdd_FillType: 390 passes[0] = &gEOStencilPass; 391 if (stencilOnly) { 392 passCount = 1; 393 lastPassIsBounds = false; 394 } else { 395 passCount = 2; 396 lastPassIsBounds = true; 397 if (reverse) { 398 passes[1] = &gInvEOColorPass; 399 } else { 400 passes[1] = &gEOColorPass; 401 } 402 } 403 drawFace[0] = drawFace[1] = GrDrawState::kBoth_DrawFace; 404 break; 405 406 case SkPath::kInverseWinding_FillType: 407 reverse = true; 408 // fallthrough 409 case SkPath::kWinding_FillType: 410 if (fSeparateStencil) { 411 if (fStencilWrapOps) { 412 passes[0] = &gWindStencilSeparateWithWrap; 413 } else { 414 passes[0] = &gWindStencilSeparateNoWrap; 415 } 416 passCount = 2; 417 drawFace[0] = GrDrawState::kBoth_DrawFace; 418 } else { 419 if (fStencilWrapOps) { 420 passes[0] = &gWindSingleStencilWithWrapInc; 421 passes[1] = &gWindSingleStencilWithWrapDec; 422 } else { 423 passes[0] = &gWindSingleStencilNoWrapInc; 424 passes[1] = &gWindSingleStencilNoWrapDec; 425 } 426 // which is cw and which is ccw is arbitrary. 427 drawFace[0] = GrDrawState::kCW_DrawFace; 428 drawFace[1] = GrDrawState::kCCW_DrawFace; 429 passCount = 3; 430 } 431 if (stencilOnly) { 432 lastPassIsBounds = false; 433 --passCount; 434 } else { 435 lastPassIsBounds = true; 436 drawFace[passCount-1] = GrDrawState::kBoth_DrawFace; 437 if (reverse) { 438 passes[passCount-1] = &gInvWindColorPass; 439 } else { 440 passes[passCount-1] = &gWindColorPass; 441 } 442 } 443 break; 444 default: 445 GrAssert(!"Unknown path fFill!"); 446 return false; 447 } 448 } 449 } 450 451 SkRect devBounds; 452 GetPathDevBounds(path, drawState->getRenderTarget(), viewM, &devBounds); 453 454 for (int p = 0; p < passCount; ++p) { 455 drawState->setDrawFace(drawFace[p]); 456 if (NULL != passes[p]) { 457 *drawState->stencil() = *passes[p]; 458 } 459 460 if (lastPassIsBounds && (p == passCount-1)) { 461 if (!colorWritesWereDisabled) { 462 drawState->disableState(GrDrawState::kNoColorWrites_StateBit); 463 } 464 GrRect bounds; 465 GrDrawState::AutoViewMatrixRestore avmr; 466 if (reverse) { 467 GrAssert(NULL != drawState->getRenderTarget()); 468 // draw over the dev bounds (which will be the whole dst surface for inv fill). 469 bounds = devBounds; 470 SkMatrix vmi; 471 // mapRect through persp matrix may not be correct 472 if (!drawState->getViewMatrix().hasPerspective() && 473 drawState->getViewInverse(&vmi)) { 474 vmi.mapRect(&bounds); 475 } else { 476 avmr.setIdentity(drawState); 477 } 478 } else { 479 bounds = path.getBounds(); 480 } 481 GrDrawTarget::AutoGeometryAndStatePush agasp(target, GrDrawTarget::kPreserve_ASRInit); 482 target->drawSimpleRect(bounds, NULL); 483 } else { 484 if (passCount > 1) { 485 drawState->enableState(GrDrawState::kNoColorWrites_StateBit); 486 } 487 if (indexCnt) { 488 target->drawIndexed(primType, 0, 0, 489 vertexCnt, indexCnt, &devBounds); 490 } else { 491 target->drawNonIndexed(primType, 0, vertexCnt, &devBounds); 492 } 493 } 494 } 495 return true; 496} 497 498bool GrDefaultPathRenderer::canDrawPath(const SkPath& path, 499 const SkStrokeRec& stroke, 500 const GrDrawTarget* target, 501 bool antiAlias) const { 502 // this class can draw any path with any fill but doesn't do any anti-aliasing. 503 return (stroke.isFillStyle() || stroke.isHairlineStyle()) && !antiAlias; 504} 505 506bool GrDefaultPathRenderer::onDrawPath(const SkPath& path, 507 const SkStrokeRec& stroke, 508 GrDrawTarget* target, 509 bool antiAlias) { 510 return this->internalDrawPath(path, 511 stroke, 512 target, 513 false); 514} 515 516void GrDefaultPathRenderer::onStencilPath(const SkPath& path, 517 const SkStrokeRec& stroke, 518 GrDrawTarget* target) { 519 GrAssert(SkPath::kInverseEvenOdd_FillType != path.getFillType()); 520 GrAssert(SkPath::kInverseWinding_FillType != path.getFillType()); 521 this->internalDrawPath(path, stroke, target, true); 522} 523