1 /*
2  * Copyright 2012 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 "GrStencilAndCoverPathRenderer.h"
9 #include "GrCaps.h"
10 #include "GrContext.h"
11 #include "GrDrawPathOp.h"
12 #include "GrFixedClip.h"
13 #include "GrGpu.h"
14 #include "GrPath.h"
15 #include "GrRenderTargetContextPriv.h"
16 #include "GrResourceProvider.h"
17 #include "GrShape.h"
18 #include "GrStencilClip.h"
19 #include "GrStencilPathOp.h"
20 #include "GrStyle.h"
21 #include "ops/GrFillRectOp.h"
22 
Create(GrResourceProvider * resourceProvider,const GrCaps & caps)23 GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrResourceProvider* resourceProvider,
24                                                       const GrCaps& caps) {
25     if (caps.shaderCaps()->pathRenderingSupport() && !caps.avoidStencilBuffers()) {
26         return new GrStencilAndCoverPathRenderer(resourceProvider);
27     } else {
28         return nullptr;
29     }
30 }
31 
GrStencilAndCoverPathRenderer(GrResourceProvider * resourceProvider)32 GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrResourceProvider* resourceProvider)
33     : fResourceProvider(resourceProvider) {
34 }
35 
36 GrPathRenderer::CanDrawPath
onCanDrawPath(const CanDrawPathArgs & args) const37 GrStencilAndCoverPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
38     SkASSERT(!args.fTargetIsWrappedVkSecondaryCB);
39     // GrPath doesn't support hairline paths. An arbitrary path effect could produce a hairline
40     // path.
41     if (args.fShape->style().strokeRec().isHairlineStyle() ||
42         args.fShape->style().hasNonDashPathEffect()) {
43         return CanDrawPath::kNo;
44     }
45     if (args.fHasUserStencilSettings) {
46         return CanDrawPath::kNo;
47     }
48     // doesn't do per-path AA, relies on the target having MSAA.
49     if (GrAAType::kCoverage == args.fAAType) {
50         return CanDrawPath::kNo;
51     }
52     return CanDrawPath::kYes;
53 }
54 
get_gr_path(GrResourceProvider * resourceProvider,const GrShape & shape)55 static sk_sp<GrPath> get_gr_path(GrResourceProvider* resourceProvider, const GrShape& shape) {
56     GrUniqueKey key;
57     bool isVolatile;
58     GrPath::ComputeKey(shape, &key, &isVolatile);
59     sk_sp<GrPath> path;
60     if (!isVolatile) {
61         path = resourceProvider->findByUniqueKey<GrPath>(key);
62     }
63     if (!path) {
64         SkPath skPath;
65         shape.asPath(&skPath);
66         path = resourceProvider->createPath(skPath, shape.style());
67         if (!isVolatile) {
68             resourceProvider->assignUniqueKeyToResource(key, path.get());
69         }
70     } else {
71 #ifdef SK_DEBUG
72         SkPath skPath;
73         shape.asPath(&skPath);
74         SkASSERT(path->isEqualTo(skPath, shape.style()));
75 #endif
76     }
77     return path;
78 }
79 
onStencilPath(const StencilPathArgs & args)80 void GrStencilAndCoverPathRenderer::onStencilPath(const StencilPathArgs& args) {
81     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
82                               "GrStencilAndCoverPathRenderer::onStencilPath");
83     sk_sp<GrPath> p(get_gr_path(fResourceProvider, *args.fShape));
84     args.fRenderTargetContext->priv().stencilPath(*args.fClip, args.fAAType,
85                                                   *args.fViewMatrix, p.get());
86 }
87 
onDrawPath(const DrawPathArgs & args)88 bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) {
89     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
90                               "GrStencilAndCoverPathRenderer::onDrawPath");
91     SkASSERT(!args.fShape->style().strokeRec().isHairlineStyle());
92 
93     const SkMatrix& viewMatrix = *args.fViewMatrix;
94 
95 
96     sk_sp<GrPath> path(get_gr_path(fResourceProvider, *args.fShape));
97 
98     if (args.fShape->inverseFilled()) {
99         SkMatrix vmi;
100         if (!viewMatrix.invert(&vmi)) {
101             return true;
102         }
103 
104         SkRect devBounds = SkRect::MakeIWH(args.fRenderTargetContext->width(),
105                                            args.fRenderTargetContext->height()); // Inverse fill.
106 
107         // fake inverse with a stencil and cover
108         GrAppliedClip appliedClip;
109         if (!args.fClip->apply(args.fContext, args.fRenderTargetContext,
110                                GrAATypeIsHW(args.fAAType), true, &appliedClip, &devBounds)) {
111             return true;
112         }
113         GrStencilClip stencilClip(appliedClip.stencilStackID());
114         if (appliedClip.scissorState().enabled()) {
115             stencilClip.fixedClip().setScissor(appliedClip.scissorState().rect());
116         }
117         if (appliedClip.windowRectsState().enabled()) {
118             stencilClip.fixedClip().setWindowRectangles(appliedClip.windowRectsState().windows(),
119                                                         appliedClip.windowRectsState().mode());
120         }
121         // Just ignore the analytic FPs (if any) during the stencil pass. They will still clip the
122         // final draw and it is meaningless to multiply by coverage when drawing to stencil.
123         args.fRenderTargetContext->priv().stencilPath(stencilClip, args.fAAType, viewMatrix,
124                                                       path.get());
125 
126         {
127             static constexpr GrUserStencilSettings kInvertedCoverPass(
128                 GrUserStencilSettings::StaticInit<
129                     0x0000,
130                     // We know our rect will hit pixels outside the clip and the user bits will
131                     // be 0 outside the clip. So we can't just fill where the user bits are 0. We
132                     // also need to check that the clip bit is set.
133                     GrUserStencilTest::kEqualIfInClip,
134                     0xffff,
135                     GrUserStencilOp::kKeep,
136                     GrUserStencilOp::kZero,
137                     0xffff>()
138             );
139 
140             SkRect coverBounds;
141             // mapRect through persp matrix may not be correct
142             if (!viewMatrix.hasPerspective()) {
143                 vmi.mapRect(&coverBounds, devBounds);
144                 // theoretically could set bloat = 0, instead leave it because of matrix inversion
145                 // precision.
146                 SkScalar bloat = viewMatrix.getMaxScale() * SK_ScalarHalf;
147                 coverBounds.outset(bloat, bloat);
148             } else {
149                 coverBounds = devBounds;
150             }
151             const SkMatrix& coverMatrix = !viewMatrix.hasPerspective() ? viewMatrix : SkMatrix::I();
152             const SkMatrix& localMatrix = !viewMatrix.hasPerspective() ? SkMatrix::I() : vmi;
153 
154             // We have to suppress enabling MSAA for mixed samples or we will get seams due to
155             // coverage modulation along the edge where two triangles making up the rect meet.
156             GrAAType coverAAType = args.fAAType;
157             if (GrAAType::kMixedSamples == coverAAType) {
158                 coverAAType = GrAAType::kNone;
159             }
160             // This is a non-coverage aa rect operation
161             SkASSERT(coverAAType == GrAAType::kNone || coverAAType == GrAAType::kMSAA);
162             std::unique_ptr<GrDrawOp> op = GrFillRectOp::MakeWithLocalMatrix(
163                                                          args.fContext, std::move(args.fPaint),
164                                                          coverAAType, coverMatrix, localMatrix,
165                                                          coverBounds, &kInvertedCoverPass);
166 
167             args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
168         }
169     } else {
170         std::unique_ptr<GrDrawOp> op =
171                 GrDrawPathOp::Make(args.fContext, viewMatrix, std::move(args.fPaint),
172                                    args.fAAType, path.get());
173         args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
174     }
175 
176     return true;
177 }
178