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