|
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 "GrContext.h" |
|
11 #include "GrDrawState.h" |
|
12 #include "GrPathUtils.h" |
|
13 #include "SkString.h" |
|
14 #include "SkStrokeRec.h" |
|
15 #include "SkTLazy.h" |
|
16 #include "SkTrace.h" |
|
17 |
|
18 |
|
19 GrDefaultPathRenderer::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 |
|
31 GR_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 |
|
40 GR_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. |
|
49 GR_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 |
|
63 GR_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. |
|
75 GR_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. |
|
87 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc, |
|
88 kIncWrap_StencilOp, |
|
89 kKeep_StencilOp, |
|
90 kAlwaysIfInClip_StencilFunc, |
|
91 0xffff, |
|
92 0xffff, |
|
93 0xffff); |
|
94 |
|
95 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec, |
|
96 kDecWrap_StencilOp, |
|
97 kKeep_StencilOp, |
|
98 kAlwaysIfInClip_StencilFunc, |
|
99 0xffff, |
|
100 0xffff, |
|
101 0xffff); |
|
102 |
|
103 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc, |
|
104 kInvert_StencilOp, |
|
105 kIncClamp_StencilOp, |
|
106 kEqual_StencilFunc, |
|
107 0xffff, |
|
108 0xffff, |
|
109 0xffff); |
|
110 |
|
111 GR_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 |
|
121 GR_STATIC_CONST_SAME_STENCIL(gWindColorPass, |
|
122 kZero_StencilOp, |
|
123 kZero_StencilOp, |
|
124 kNonZeroIfInClip_StencilFunc, |
|
125 0xffff, |
|
126 0x0000, |
|
127 0xffff); |
|
128 |
|
129 GR_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. |
|
141 GR_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 |
|
154 static 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 |
|
165 GrPathRenderer::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 |
|
176 static 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 |
|
190 bool 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 SkASSERT(NULL != base); |
|
246 GrPoint* vert = base; |
|
247 |
|
248 GrPoint pts[4]; |
|
249 |
|
250 bool first = true; |
|
251 int subpath = 0; |
|
252 |
|
253 SkPath::Iter iter(path, false); |
|
254 |
|
255 for (;;) { |
|
256 SkPath::Verb verb = iter.next(pts); |
|
257 switch (verb) { |
|
258 case SkPath::kConic_Verb: |
|
259 SkASSERT(0); |
|
260 break; |
|
261 case SkPath::kMove_Verb: |
|
262 if (!first) { |
|
263 uint16_t currIdx = (uint16_t) (vert - base); |
|
264 subpathIdxStart = currIdx; |
|
265 ++subpath; |
|
266 } |
|
267 *vert = pts[0]; |
|
268 vert++; |
|
269 break; |
|
270 case SkPath::kLine_Verb: |
|
271 if (indexed) { |
|
272 uint16_t prevIdx = (uint16_t)(vert - base) - 1; |
|
273 append_countour_edge_indices(isHairline, subpathIdxStart, |
|
274 prevIdx, &idx); |
|
275 } |
|
276 *(vert++) = pts[1]; |
|
277 break; |
|
278 case SkPath::kQuad_Verb: { |
|
279 // first pt of quad is the pt we ended on in previous step |
|
280 uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1; |
|
281 uint16_t numPts = (uint16_t) |
|
282 GrPathUtils::generateQuadraticPoints( |
|
283 pts[0], pts[1], pts[2], |
|
284 srcSpaceTolSqd, &vert, |
|
285 GrPathUtils::quadraticPointCount(pts, srcSpaceTol)); |
|
286 if (indexed) { |
|
287 for (uint16_t i = 0; i < numPts; ++i) { |
|
288 append_countour_edge_indices(isHairline, subpathIdxStart, |
|
289 firstQPtIdx + i, &idx); |
|
290 } |
|
291 } |
|
292 break; |
|
293 } |
|
294 case SkPath::kCubic_Verb: { |
|
295 // first pt of cubic is the pt we ended on in previous step |
|
296 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1; |
|
297 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( |
|
298 pts[0], pts[1], pts[2], pts[3], |
|
299 srcSpaceTolSqd, &vert, |
|
300 GrPathUtils::cubicPointCount(pts, srcSpaceTol)); |
|
301 if (indexed) { |
|
302 for (uint16_t i = 0; i < numPts; ++i) { |
|
303 append_countour_edge_indices(isHairline, subpathIdxStart, |
|
304 firstCPtIdx + i, &idx); |
|
305 } |
|
306 } |
|
307 break; |
|
308 } |
|
309 case SkPath::kClose_Verb: |
|
310 break; |
|
311 case SkPath::kDone_Verb: |
|
312 // uint16_t currIdx = (uint16_t) (vert - base); |
|
313 goto FINISHED; |
|
314 } |
|
315 first = false; |
|
316 } |
|
317 FINISHED: |
|
318 SkASSERT((vert - base) <= maxPts); |
|
319 SkASSERT((idx - idxBase) <= maxIdxs); |
|
320 |
|
321 *vertexCnt = static_cast<int>(vert - base); |
|
322 *indexCnt = static_cast<int>(idx - idxBase); |
|
323 |
|
324 } |
|
325 return true; |
|
326 } |
|
327 |
|
328 bool GrDefaultPathRenderer::internalDrawPath(const SkPath& path, |
|
329 const SkStrokeRec& origStroke, |
|
330 GrDrawTarget* target, |
|
331 bool stencilOnly) { |
|
332 |
|
333 SkMatrix viewM = target->getDrawState().getViewMatrix(); |
|
334 SkTCopyOnFirstWrite<SkStrokeRec> stroke(origStroke); |
|
335 |
|
336 SkScalar hairlineCoverage; |
|
337 if (IsStrokeHairlineOrEquivalent(*stroke, target->getDrawState().getViewMatrix(), |
|
338 &hairlineCoverage)) { |
|
339 uint8_t newCoverage = SkScalarRoundToInt(hairlineCoverage * |
|
340 target->getDrawState().getCoverage()); |
|
341 target->drawState()->setCoverage(newCoverage); |
|
342 |
|
343 if (!stroke->isHairlineStyle()) { |
|
344 stroke.writable()->setHairlineStyle(); |
|
345 } |
|
346 } |
|
347 |
|
348 SkScalar tol = SK_Scalar1; |
|
349 tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds()); |
|
350 |
|
351 int vertexCnt; |
|
352 int indexCnt; |
|
353 GrPrimitiveType primType; |
|
354 GrDrawTarget::AutoReleaseGeometry arg; |
|
355 if (!this->createGeom(path, |
|
356 *stroke, |
|
357 tol, |
|
358 target, |
|
359 &primType, |
|
360 &vertexCnt, |
|
361 &indexCnt, |
|
362 &arg)) { |
|
363 return false; |
|
364 } |
|
365 |
|
366 SkASSERT(NULL != target); |
|
367 GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit); |
|
368 GrDrawState* drawState = target->drawState(); |
|
369 bool colorWritesWereDisabled = drawState->isColorWriteDisabled(); |
|
370 // face culling doesn't make sense here |
|
371 SkASSERT(GrDrawState::kBoth_DrawFace == drawState->getDrawFace()); |
|
372 |
|
373 int passCount = 0; |
|
374 const GrStencilSettings* passes[3]; |
|
375 GrDrawState::DrawFace drawFace[3]; |
|
376 bool reverse = false; |
|
377 bool lastPassIsBounds; |
|
378 |
|
379 if (stroke->isHairlineStyle()) { |
|
380 passCount = 1; |
|
381 if (stencilOnly) { |
|
382 passes[0] = &gDirectToStencil; |
|
383 } else { |
|
384 passes[0] = NULL; |
|
385 } |
|
386 lastPassIsBounds = false; |
|
387 drawFace[0] = GrDrawState::kBoth_DrawFace; |
|
388 } else { |
|
389 if (single_pass_path(path, *stroke)) { |
|
390 passCount = 1; |
|
391 if (stencilOnly) { |
|
392 passes[0] = &gDirectToStencil; |
|
393 } else { |
|
394 passes[0] = NULL; |
|
395 } |
|
396 drawFace[0] = GrDrawState::kBoth_DrawFace; |
|
397 lastPassIsBounds = false; |
|
398 } else { |
|
399 switch (path.getFillType()) { |
|
400 case SkPath::kInverseEvenOdd_FillType: |
|
401 reverse = true; |
|
402 // fallthrough |
|
403 case SkPath::kEvenOdd_FillType: |
|
404 passes[0] = &gEOStencilPass; |
|
405 if (stencilOnly) { |
|
406 passCount = 1; |
|
407 lastPassIsBounds = false; |
|
408 } else { |
|
409 passCount = 2; |
|
410 lastPassIsBounds = true; |
|
411 if (reverse) { |
|
412 passes[1] = &gInvEOColorPass; |
|
413 } else { |
|
414 passes[1] = &gEOColorPass; |
|
415 } |
|
416 } |
|
417 drawFace[0] = drawFace[1] = GrDrawState::kBoth_DrawFace; |
|
418 break; |
|
419 |
|
420 case SkPath::kInverseWinding_FillType: |
|
421 reverse = true; |
|
422 // fallthrough |
|
423 case SkPath::kWinding_FillType: |
|
424 if (fSeparateStencil) { |
|
425 if (fStencilWrapOps) { |
|
426 passes[0] = &gWindStencilSeparateWithWrap; |
|
427 } else { |
|
428 passes[0] = &gWindStencilSeparateNoWrap; |
|
429 } |
|
430 passCount = 2; |
|
431 drawFace[0] = GrDrawState::kBoth_DrawFace; |
|
432 } else { |
|
433 if (fStencilWrapOps) { |
|
434 passes[0] = &gWindSingleStencilWithWrapInc; |
|
435 passes[1] = &gWindSingleStencilWithWrapDec; |
|
436 } else { |
|
437 passes[0] = &gWindSingleStencilNoWrapInc; |
|
438 passes[1] = &gWindSingleStencilNoWrapDec; |
|
439 } |
|
440 // which is cw and which is ccw is arbitrary. |
|
441 drawFace[0] = GrDrawState::kCW_DrawFace; |
|
442 drawFace[1] = GrDrawState::kCCW_DrawFace; |
|
443 passCount = 3; |
|
444 } |
|
445 if (stencilOnly) { |
|
446 lastPassIsBounds = false; |
|
447 --passCount; |
|
448 } else { |
|
449 lastPassIsBounds = true; |
|
450 drawFace[passCount-1] = GrDrawState::kBoth_DrawFace; |
|
451 if (reverse) { |
|
452 passes[passCount-1] = &gInvWindColorPass; |
|
453 } else { |
|
454 passes[passCount-1] = &gWindColorPass; |
|
455 } |
|
456 } |
|
457 break; |
|
458 default: |
|
459 SkDEBUGFAIL("Unknown path fFill!"); |
|
460 return false; |
|
461 } |
|
462 } |
|
463 } |
|
464 |
|
465 SkRect devBounds; |
|
466 GetPathDevBounds(path, drawState->getRenderTarget(), viewM, &devBounds); |
|
467 |
|
468 for (int p = 0; p < passCount; ++p) { |
|
469 drawState->setDrawFace(drawFace[p]); |
|
470 if (NULL != passes[p]) { |
|
471 *drawState->stencil() = *passes[p]; |
|
472 } |
|
473 |
|
474 if (lastPassIsBounds && (p == passCount-1)) { |
|
475 if (!colorWritesWereDisabled) { |
|
476 drawState->disableState(GrDrawState::kNoColorWrites_StateBit); |
|
477 } |
|
478 SkRect bounds; |
|
479 GrDrawState::AutoViewMatrixRestore avmr; |
|
480 if (reverse) { |
|
481 SkASSERT(NULL != drawState->getRenderTarget()); |
|
482 // draw over the dev bounds (which will be the whole dst surface for inv fill). |
|
483 bounds = devBounds; |
|
484 SkMatrix vmi; |
|
485 // mapRect through persp matrix may not be correct |
|
486 if (!drawState->getViewMatrix().hasPerspective() && |
|
487 drawState->getViewInverse(&vmi)) { |
|
488 vmi.mapRect(&bounds); |
|
489 } else { |
|
490 avmr.setIdentity(drawState); |
|
491 } |
|
492 } else { |
|
493 bounds = path.getBounds(); |
|
494 } |
|
495 GrDrawTarget::AutoGeometryAndStatePush agasp(target, GrDrawTarget::kPreserve_ASRInit); |
|
496 target->drawSimpleRect(bounds, NULL); |
|
497 } else { |
|
498 if (passCount > 1) { |
|
499 drawState->enableState(GrDrawState::kNoColorWrites_StateBit); |
|
500 } |
|
501 if (indexCnt) { |
|
502 target->drawIndexed(primType, 0, 0, |
|
503 vertexCnt, indexCnt, &devBounds); |
|
504 } else { |
|
505 target->drawNonIndexed(primType, 0, vertexCnt, &devBounds); |
|
506 } |
|
507 } |
|
508 } |
|
509 return true; |
|
510 } |
|
511 |
|
512 bool GrDefaultPathRenderer::canDrawPath(const SkPath& path, |
|
513 const SkStrokeRec& stroke, |
|
514 const GrDrawTarget* target, |
|
515 bool antiAlias) const { |
|
516 // this class can draw any path with any fill but doesn't do any anti-aliasing. |
|
517 |
|
518 return !antiAlias && |
|
519 (stroke.isFillStyle() || |
|
520 IsStrokeHairlineOrEquivalent(stroke, target->getDrawState().getViewMatrix(), NULL)); |
|
521 } |
|
522 |
|
523 bool GrDefaultPathRenderer::onDrawPath(const SkPath& path, |
|
524 const SkStrokeRec& stroke, |
|
525 GrDrawTarget* target, |
|
526 bool antiAlias) { |
|
527 return this->internalDrawPath(path, |
|
528 stroke, |
|
529 target, |
|
530 false); |
|
531 } |
|
532 |
|
533 void GrDefaultPathRenderer::onStencilPath(const SkPath& path, |
|
534 const SkStrokeRec& stroke, |
|
535 GrDrawTarget* target) { |
|
536 SkASSERT(SkPath::kInverseEvenOdd_FillType != path.getFillType()); |
|
537 SkASSERT(SkPath::kInverseWinding_FillType != path.getFillType()); |
|
538 this->internalDrawPath(path, stroke, target, true); |
|
539 } |