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 |