• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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