1 
2 /*
3  * Copyright 2012 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 #include "GrStencilAndCoverPathRenderer.h"
11 #include "GrContext.h"
12 #include "GrDrawTargetCaps.h"
13 #include "GrGpu.h"
14 #include "GrPath.h"
15 #include "GrRenderTarget.h"
16 #include "GrRenderTargetPriv.h"
17 #include "GrResourceProvider.h"
18 #include "GrStrokeInfo.h"
19 
20 /*
21  * For now paths only natively support winding and even odd fill types
22  */
convert_skpath_filltype(SkPath::FillType fill)23 static GrPathRendering::FillType convert_skpath_filltype(SkPath::FillType fill) {
24     switch (fill) {
25         default:
26             SkFAIL("Incomplete Switch\n");
27         case SkPath::kWinding_FillType:
28         case SkPath::kInverseWinding_FillType:
29             return GrPathRendering::kWinding_FillType;
30         case SkPath::kEvenOdd_FillType:
31         case SkPath::kInverseEvenOdd_FillType:
32             return GrPathRendering::kEvenOdd_FillType;
33     }
34 }
35 
Create(GrContext * context)36 GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrContext* context) {
37     SkASSERT(context);
38     SkASSERT(context->getGpu());
39     if (context->getGpu()->caps()->shaderCaps()->pathRenderingSupport()) {
40         return SkNEW_ARGS(GrStencilAndCoverPathRenderer, (context->getGpu()));
41     } else {
42         return NULL;
43     }
44 }
45 
GrStencilAndCoverPathRenderer(GrGpu * gpu)46 GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrGpu* gpu) {
47     SkASSERT(gpu->caps()->shaderCaps()->pathRenderingSupport());
48     fGpu = gpu;
49     gpu->ref();
50 }
51 
~GrStencilAndCoverPathRenderer()52 GrStencilAndCoverPathRenderer::~GrStencilAndCoverPathRenderer() {
53     fGpu->unref();
54 }
55 
canDrawPath(const GrDrawTarget * target,const GrPipelineBuilder * pipelineBuilder,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & stroke,bool antiAlias) const56 bool GrStencilAndCoverPathRenderer::canDrawPath(const GrDrawTarget* target,
57                                                 const GrPipelineBuilder* pipelineBuilder,
58                                                 const SkMatrix& viewMatrix,
59                                                 const SkPath& path,
60                                                 const GrStrokeInfo& stroke,
61                                                 bool antiAlias) const {
62     return !stroke.getStrokeRec().isHairlineStyle() &&
63         !stroke.isDashed() &&
64         !antiAlias && // doesn't do per-path AA, relies on the target having MSAA
65         pipelineBuilder->getStencil().isDisabled();
66 }
67 
68 GrPathRenderer::StencilSupport
onGetStencilSupport(const GrDrawTarget *,const GrPipelineBuilder *,const SkPath &,const GrStrokeInfo &) const69 GrStencilAndCoverPathRenderer::onGetStencilSupport(const GrDrawTarget*,
70                                                    const GrPipelineBuilder*,
71                                                    const SkPath&,
72                                                    const GrStrokeInfo&) const {
73     return GrPathRenderer::kStencilOnly_StencilSupport;
74 }
75 
get_gr_path(GrGpu * gpu,const SkPath & skPath,const SkStrokeRec & stroke)76 static GrPath* get_gr_path(GrGpu* gpu, const SkPath& skPath, const SkStrokeRec& stroke) {
77     GrContext* ctx = gpu->getContext();
78     GrUniqueKey key;
79     GrPath::ComputeKey(skPath, stroke, &key);
80     SkAutoTUnref<GrPath> path(
81         static_cast<GrPath*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key)));
82     if (NULL == path || !path->isEqualTo(skPath, stroke)) {
83         path.reset(gpu->pathRendering()->createPath(skPath, stroke));
84         ctx->resourceProvider()->assignUniqueKeyToResource(key, path);
85     }
86     return path.detach();
87 }
88 
onStencilPath(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & stroke)89 void GrStencilAndCoverPathRenderer::onStencilPath(GrDrawTarget* target,
90                                                   GrPipelineBuilder* pipelineBuilder,
91                                                   const SkMatrix& viewMatrix,
92                                                   const SkPath& path,
93                                                   const GrStrokeInfo& stroke) {
94     SkASSERT(!path.isInverseFillType());
95     SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(GrColor_WHITE, viewMatrix));
96     SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke.getStrokeRec()));
97     target->stencilPath(pipelineBuilder, pp, p, convert_skpath_filltype(path.getFillType()));
98 }
99 
onDrawPath(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & stroke,bool antiAlias)100 bool GrStencilAndCoverPathRenderer::onDrawPath(GrDrawTarget* target,
101                                                GrPipelineBuilder* pipelineBuilder,
102                                                GrColor color,
103                                                const SkMatrix& viewMatrix,
104                                                const SkPath& path,
105                                                const GrStrokeInfo& stroke,
106                                                bool antiAlias) {
107     SkASSERT(!antiAlias);
108     SkASSERT(!stroke.getStrokeRec().isHairlineStyle());
109     SkASSERT(!stroke.isDashed());
110     SkASSERT(pipelineBuilder->getStencil().isDisabled());
111 
112     SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke.getStrokeRec()));
113 
114     if (path.isInverseFillType()) {
115         GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass,
116             kZero_StencilOp,
117             kZero_StencilOp,
118             // We know our rect will hit pixels outside the clip and the user bits will be 0
119             // outside the clip. So we can't just fill where the user bits are 0. We also need to
120             // check that the clip bit is set.
121             kEqualIfInClip_StencilFunc,
122             0xffff,
123             0x0000,
124             0xffff);
125 
126         pipelineBuilder->setStencil(kInvertedStencilPass);
127 
128         // fake inverse with a stencil and cover
129         SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(GrColor_WHITE, viewMatrix));
130         target->stencilPath(pipelineBuilder, pp, p, convert_skpath_filltype(path.getFillType()));
131 
132         SkMatrix invert = SkMatrix::I();
133         SkRect bounds =
134             SkRect::MakeLTRB(0, 0, SkIntToScalar(pipelineBuilder->getRenderTarget()->width()),
135                              SkIntToScalar(pipelineBuilder->getRenderTarget()->height()));
136         SkMatrix vmi;
137         // mapRect through persp matrix may not be correct
138         if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
139             vmi.mapRect(&bounds);
140             // theoretically could set bloat = 0, instead leave it because of matrix inversion
141             // precision.
142             SkScalar bloat = viewMatrix.getMaxScale() * SK_ScalarHalf;
143             bounds.outset(bloat, bloat);
144         } else {
145             if (!viewMatrix.invert(&invert)) {
146                 return false;
147             }
148         }
149         const SkMatrix& viewM = viewMatrix.hasPerspective() ? SkMatrix::I() : viewMatrix;
150         target->drawRect(pipelineBuilder, color, viewM, bounds, NULL, &invert);
151     } else {
152         GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
153             kZero_StencilOp,
154             kZero_StencilOp,
155             kNotEqual_StencilFunc,
156             0xffff,
157             0x0000,
158             0xffff);
159 
160         pipelineBuilder->setStencil(kStencilPass);
161         SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(color, viewMatrix));
162         target->drawPath(pipelineBuilder, pp, p, convert_skpath_filltype(path.getFillType()));
163     }
164 
165     pipelineBuilder->stencil()->setDisabled();
166     return true;
167 }
168