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
GrDefaultPathRenderer(bool separateStencilSupport,bool stencilWrapOpsSupport)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
single_pass_path(const SkPath & path,const SkStrokeRec & stroke)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
onGetStencilSupport(const SkPath & path,const SkStrokeRec & stroke,const GrDrawTarget *) const165 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
append_countour_edge_indices(bool hairLine,uint16_t fanCenterIdx,uint16_t edgeV0Idx,uint16_t ** indices)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
createGeom(const SkPath & path,const SkStrokeRec & stroke,SkScalar srcSpaceTol,GrDrawTarget * target,GrPrimitiveType * primType,int * vertexCnt,int * indexCnt,GrDrawTarget::AutoReleaseGeometry * arg)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
internalDrawPath(const SkPath & path,const SkStrokeRec & origStroke,GrDrawTarget * target,bool stencilOnly)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
canDrawPath(const SkPath & path,const SkStrokeRec & stroke,const GrDrawTarget * target,bool antiAlias) const512 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
onDrawPath(const SkPath & path,const SkStrokeRec & stroke,GrDrawTarget * target,bool antiAlias)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
onStencilPath(const SkPath & path,const SkStrokeRec & stroke,GrDrawTarget * target)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 }
540