GrDefaultPathRenderer.cpp revision f438c972589f91fec69b734e9f334d1fa2aa9aa8
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::kMove_Verb: 259 if (!first) { 260 uint16_t currIdx = (uint16_t) (vert - base); 261 subpathIdxStart = currIdx; 262 ++subpath; 263 } 264 *vert = pts[0]; 265 vert++; 266 break; 267 case SkPath::kLine_Verb: 268 if (indexed) { 269 uint16_t prevIdx = (uint16_t)(vert - base) - 1; 270 append_countour_edge_indices(isHairline, subpathIdxStart, 271 prevIdx, &idx); 272 } 273 *(vert++) = pts[1]; 274 break; 275 case SkPath::kQuad_Verb: { 276 // first pt of quad is the pt we ended on in previous step 277 uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1; 278 uint16_t numPts = (uint16_t) 279 GrPathUtils::generateQuadraticPoints( 280 pts[0], pts[1], pts[2], 281 srcSpaceTolSqd, &vert, 282 GrPathUtils::quadraticPointCount(pts, srcSpaceTol)); 283 if (indexed) { 284 for (uint16_t i = 0; i < numPts; ++i) { 285 append_countour_edge_indices(isHairline, subpathIdxStart, 286 firstQPtIdx + i, &idx); 287 } 288 } 289 break; 290 } 291 case SkPath::kCubic_Verb: { 292 // first pt of cubic is the pt we ended on in previous step 293 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1; 294 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( 295 pts[0], pts[1], pts[2], pts[3], 296 srcSpaceTolSqd, &vert, 297 GrPathUtils::cubicPointCount(pts, srcSpaceTol)); 298 if (indexed) { 299 for (uint16_t i = 0; i < numPts; ++i) { 300 append_countour_edge_indices(isHairline, subpathIdxStart, 301 firstCPtIdx + i, &idx); 302 } 303 } 304 break; 305 } 306 case SkPath::kClose_Verb: 307 break; 308 case SkPath::kDone_Verb: 309 // uint16_t currIdx = (uint16_t) (vert - base); 310 goto FINISHED; 311 } 312 first = false; 313 } 314FINISHED: 315 GrAssert((vert - base) <= maxPts); 316 GrAssert((idx - idxBase) <= maxIdxs); 317 318 *vertexCnt = vert - base; 319 *indexCnt = idx - idxBase; 320 321 } 322 return true; 323} 324 325bool GrDefaultPathRenderer::internalDrawPath(const SkPath& path, 326 const SkStrokeRec& stroke, 327 GrDrawTarget* target, 328 bool stencilOnly) { 329 330 SkMatrix viewM = target->getDrawState().getViewMatrix(); 331 SkScalar tol = SK_Scalar1; 332 tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds()); 333 334 int vertexCnt; 335 int indexCnt; 336 GrPrimitiveType primType; 337 GrDrawTarget::AutoReleaseGeometry arg; 338 if (!this->createGeom(path, 339 stroke, 340 tol, 341 target, 342 &primType, 343 &vertexCnt, 344 &indexCnt, 345 &arg)) { 346 return false; 347 } 348 349 GrAssert(NULL != target); 350 GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit); 351 GrDrawState* drawState = target->drawState(); 352 bool colorWritesWereDisabled = drawState->isColorWriteDisabled(); 353 // face culling doesn't make sense here 354 GrAssert(GrDrawState::kBoth_DrawFace == drawState->getDrawFace()); 355 356 int passCount = 0; 357 const GrStencilSettings* passes[3]; 358 GrDrawState::DrawFace drawFace[3]; 359 bool reverse = false; 360 bool lastPassIsBounds; 361 362 if (stroke.isHairlineStyle()) { 363 passCount = 1; 364 if (stencilOnly) { 365 passes[0] = &gDirectToStencil; 366 } else { 367 passes[0] = NULL; 368 } 369 lastPassIsBounds = false; 370 drawFace[0] = GrDrawState::kBoth_DrawFace; 371 } else { 372 if (single_pass_path(path, stroke)) { 373 passCount = 1; 374 if (stencilOnly) { 375 passes[0] = &gDirectToStencil; 376 } else { 377 passes[0] = NULL; 378 } 379 drawFace[0] = GrDrawState::kBoth_DrawFace; 380 lastPassIsBounds = false; 381 } else { 382 switch (path.getFillType()) { 383 case SkPath::kInverseEvenOdd_FillType: 384 reverse = true; 385 // fallthrough 386 case SkPath::kEvenOdd_FillType: 387 passes[0] = &gEOStencilPass; 388 if (stencilOnly) { 389 passCount = 1; 390 lastPassIsBounds = false; 391 } else { 392 passCount = 2; 393 lastPassIsBounds = true; 394 if (reverse) { 395 passes[1] = &gInvEOColorPass; 396 } else { 397 passes[1] = &gEOColorPass; 398 } 399 } 400 drawFace[0] = drawFace[1] = GrDrawState::kBoth_DrawFace; 401 break; 402 403 case SkPath::kInverseWinding_FillType: 404 reverse = true; 405 // fallthrough 406 case SkPath::kWinding_FillType: 407 if (fSeparateStencil) { 408 if (fStencilWrapOps) { 409 passes[0] = &gWindStencilSeparateWithWrap; 410 } else { 411 passes[0] = &gWindStencilSeparateNoWrap; 412 } 413 passCount = 2; 414 drawFace[0] = GrDrawState::kBoth_DrawFace; 415 } else { 416 if (fStencilWrapOps) { 417 passes[0] = &gWindSingleStencilWithWrapInc; 418 passes[1] = &gWindSingleStencilWithWrapDec; 419 } else { 420 passes[0] = &gWindSingleStencilNoWrapInc; 421 passes[1] = &gWindSingleStencilNoWrapDec; 422 } 423 // which is cw and which is ccw is arbitrary. 424 drawFace[0] = GrDrawState::kCW_DrawFace; 425 drawFace[1] = GrDrawState::kCCW_DrawFace; 426 passCount = 3; 427 } 428 if (stencilOnly) { 429 lastPassIsBounds = false; 430 --passCount; 431 } else { 432 lastPassIsBounds = true; 433 drawFace[passCount-1] = GrDrawState::kBoth_DrawFace; 434 if (reverse) { 435 passes[passCount-1] = &gInvWindColorPass; 436 } else { 437 passes[passCount-1] = &gWindColorPass; 438 } 439 } 440 break; 441 default: 442 GrAssert(!"Unknown path fFill!"); 443 return false; 444 } 445 } 446 } 447 448 SkRect devBounds; 449 GetPathDevBounds(path, drawState->getRenderTarget(), viewM, &devBounds); 450 451 for (int p = 0; p < passCount; ++p) { 452 drawState->setDrawFace(drawFace[p]); 453 if (NULL != passes[p]) { 454 *drawState->stencil() = *passes[p]; 455 } 456 457 if (lastPassIsBounds && (p == passCount-1)) { 458 if (!colorWritesWereDisabled) { 459 drawState->disableState(GrDrawState::kNoColorWrites_StateBit); 460 } 461 GrRect bounds; 462 GrDrawState::AutoDeviceCoordDraw adcd; 463 if (reverse) { 464 GrAssert(NULL != drawState->getRenderTarget()); 465 // draw over the dev bounds (which will be the whole dst surface for inv fill). 466 bounds = devBounds; 467 SkMatrix vmi; 468 // mapRect through persp matrix may not be correct 469 if (!drawState->getViewMatrix().hasPerspective() && 470 drawState->getViewInverse(&vmi)) { 471 vmi.mapRect(&bounds); 472 } else { 473 adcd.set(drawState); 474 } 475 } else { 476 bounds = path.getBounds(); 477 } 478 GrDrawTarget::AutoGeometryAndStatePush agasp(target, GrDrawTarget::kPreserve_ASRInit); 479 target->drawSimpleRect(bounds, NULL); 480 } else { 481 if (passCount > 1) { 482 drawState->enableState(GrDrawState::kNoColorWrites_StateBit); 483 } 484 if (indexCnt) { 485 target->drawIndexed(primType, 0, 0, 486 vertexCnt, indexCnt, &devBounds); 487 } else { 488 target->drawNonIndexed(primType, 0, vertexCnt, &devBounds); 489 } 490 } 491 } 492 return true; 493} 494 495bool GrDefaultPathRenderer::canDrawPath(const SkPath& path, 496 const SkStrokeRec& stroke, 497 const GrDrawTarget* target, 498 bool antiAlias) const { 499 // this class can draw any path with any fill but doesn't do any anti-aliasing. 500 return (stroke.isFillStyle() || stroke.isHairlineStyle()) && !antiAlias; 501} 502 503bool GrDefaultPathRenderer::onDrawPath(const SkPath& path, 504 const SkStrokeRec& stroke, 505 GrDrawTarget* target, 506 bool antiAlias) { 507 return this->internalDrawPath(path, 508 stroke, 509 target, 510 false); 511} 512 513void GrDefaultPathRenderer::onStencilPath(const SkPath& path, 514 const SkStrokeRec& stroke, 515 GrDrawTarget* target) { 516 GrAssert(SkPath::kInverseEvenOdd_FillType != path.getFillType()); 517 GrAssert(SkPath::kInverseWinding_FillType != path.getFillType()); 518 this->internalDrawPath(path, stroke, target, true); 519} 520