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
GrDefaultPathRenderer(bool separateStencilSupport,bool stencilWrapOpsSupport)24 GrDefaultPathRenderer::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
36 GR_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
45 GR_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.
54 GR_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
68 GR_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.
80 GR_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.
92 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
93 kIncWrap_StencilOp,
94 kKeep_StencilOp,
95 kAlwaysIfInClip_StencilFunc,
96 0xffff,
97 0xffff,
98 0xffff);
99
100 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
101 kDecWrap_StencilOp,
102 kKeep_StencilOp,
103 kAlwaysIfInClip_StencilFunc,
104 0xffff,
105 0xffff,
106 0xffff);
107
108 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
109 kInvert_StencilOp,
110 kIncClamp_StencilOp,
111 kEqual_StencilFunc,
112 0xffff,
113 0xffff,
114 0xffff);
115
116 GR_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
126 GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
127 kZero_StencilOp,
128 kZero_StencilOp,
129 kNonZeroIfInClip_StencilFunc,
130 0xffff,
131 0x0000,
132 0xffff);
133
134 GR_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.
146 GR_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
single_pass_path(const SkPath & path,const SkStrokeRec & stroke)159 static 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
170 GrPathRenderer::StencilSupport
onGetStencilSupport(const GrDrawTarget *,const GrPipelineBuilder *,const SkPath & path,const GrStrokeInfo & stroke) const171 GrDefaultPathRenderer::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
append_countour_edge_indices(bool hairLine,uint16_t fanCenterIdx,uint16_t edgeV0Idx,uint16_t ** indices)182 static 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
add_quad(SkPoint ** vert,const SkPoint * base,const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol,bool indexed,bool isHairline,uint16_t subpathIdxStart,int offset,uint16_t ** idx)196 static 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
214 class DefaultPathBatch : public GrBatch {
215 public:
216 struct Geometry {
217 GrColor fColor;
218 SkPath fPath;
219 SkScalar fTolerance;
220 };
221
Create(const Geometry & geometry,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,const SkRect & devBounds)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
name() const228 const char* name() const override { return "DefaultPathBatch"; }
229
getInvariantOutputColor(GrInitInvariantOutput * out) const230 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 }
getInvariantOutputCoverage(GrInitInvariantOutput * out) const234 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
235 out->setKnownSingleComponent(this->coverage());
236 }
237
initBatchTracker(const GrPipelineInfo & init)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
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)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
geoData()381 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
382
383 private:
DefaultPathBatch(const Geometry & geometry,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,const SkRect & devBounds)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
onCombineIfPossible(GrBatch * t)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
createGeom(void * vertices,size_t vertexOffset,void * indices,size_t indexOffset,int * vertexCnt,int * indexCnt,const SkPath & path,SkScalar srcSpaceTol,bool isIndexed)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
color() const516 GrColor color() const { return fBatch.fColor; }
coverage() const517 uint8_t coverage() const { return fBatch.fCoverage; }
usesLocalCoords() const518 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const519 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
isHairline() const520 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
internalDrawPath(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & origStroke,bool stencilOnly)536 bool 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
canDrawPath(const GrDrawTarget * target,const GrPipelineBuilder * pipelineBuilder,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & stroke,bool antiAlias) const709 bool 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
onDrawPath(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & stroke,bool antiAlias)721 bool 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
onStencilPath(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & stroke)737 void 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
BATCH_TEST_DEFINE(DefaultPathBatch)751 BATCH_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