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#ifndef GrPathRenderer_DEFINED
9#define GrPathRenderer_DEFINED
10
11#include "GrCaps.h"
12#include "GrRenderTargetContext.h"
13#include "GrPaint.h"
14#include "GrShape.h"
15#include "GrUserStencilSettings.h"
16
17#include "SkDrawProcs.h"
18#include "SkTArray.h"
19
20class SkPath;
21class GrFixedClip;
22struct GrPoint;
23
24/**
25 *  Base class for drawing paths into a GrOpList.
26 */
27class SK_API GrPathRenderer : public SkRefCnt {
28public:
29    GrPathRenderer();
30
31    /**
32     * A caller may wish to use a path renderer to draw a path into the stencil buffer. However,
33     * the path renderer itself may require use of the stencil buffer. Also a path renderer may
34     * use a GrProcessor coverage stage that sets coverage to zero to eliminate pixels that are
35     * covered by bounding geometry but outside the path. These exterior pixels would still be
36     * rendered into the stencil.
37     *
38     * A GrPathRenderer can provide three levels of support for stenciling paths:
39     * 1) kNoRestriction: This is the most general. The caller passes a GrPaint and calls drawPath().
40     *                    The path is rendered exactly as the draw state indicates including support
41     *                    for simultaneous color and stenciling with arbitrary stenciling rules.
42     *                    Pixels partially covered by AA paths are affected by the stencil settings.
43     * 2) kStencilOnly: The path renderer cannot apply arbitrary stencil rules nor shade and stencil
44     *                  simultaneously. The path renderer does support the stencilPath() function
45     *                  which performs no color writes and writes a non-zero stencil value to pixels
46     *                  covered by the path.
47     * 3) kNoSupport: This path renderer cannot be used to stencil the path.
48     */
49    enum StencilSupport {
50        kNoSupport_StencilSupport,
51        kStencilOnly_StencilSupport,
52        kNoRestriction_StencilSupport,
53    };
54
55    /**
56     * This function is to get the stencil support for a particular path. The path's fill must
57     * not be an inverse type. The path will always be filled and not stroked.
58     *
59     * @param shape   the shape that will be drawn. Must be simple fill styled and non-inverse
60     *                filled.
61     */
62    StencilSupport getStencilSupport(const GrShape& shape) const {
63        SkDEBUGCODE(SkPath path;)
64        SkDEBUGCODE(shape.asPath(&path);)
65        SkASSERT(shape.style().isSimpleFill());
66        SkASSERT(!path.isInverseFillType());
67        return this->onGetStencilSupport(shape);
68    }
69
70    /** Args to canDrawPath()
71     *
72     * fCaps             The context caps
73     * fPipelineBuilder  The pipelineBuilder
74     * fViewMatrix       The viewMatrix
75     * fShape            The shape to draw
76     * fAntiAlias        The type of anti aliasing required.
77     */
78    struct CanDrawPathArgs {
79        const GrCaps*               fCaps;
80        const SkMatrix*             fViewMatrix;
81        const GrShape*              fShape;
82        GrAAType                    fAAType;
83
84        // These next two are only used by GrStencilAndCoverPathRenderer
85        bool                        fHasUserStencilSettings;
86
87#ifdef SK_DEBUG
88        void validate() const {
89            SkASSERT(fCaps);
90            SkASSERT(fViewMatrix);
91            SkASSERT(fShape);
92        }
93#endif
94    };
95
96    /**
97     * Returns true if this path renderer is able to render the path. Returning false allows the
98     * caller to fallback to another path renderer This function is called when searching for a path
99     * renderer capable of rendering a path.
100     *
101     * @return  true if the path can be drawn by this object, false otherwise.
102     */
103    bool canDrawPath(const CanDrawPathArgs& args) const {
104        SkDEBUGCODE(args.validate();)
105        return this->onCanDrawPath(args);
106    }
107
108    /**
109     * Args to drawPath()
110     *
111     * fTarget                The target that the path will be rendered to
112     * fResourceProvider      The resource provider for creating gpu resources to render the path
113     * fPipelineBuilder       The pipelineBuilder
114     * fClip                  The clip
115     * fColor                 Color to render with
116     * fViewMatrix            The viewMatrix
117     * fShape                 The shape to draw
118     * fAAtype                true if anti-aliasing is required.
119     * fGammaCorrect          true if gamma-correct rendering is to be used.
120     */
121    struct DrawPathArgs {
122        GrContext*                   fContext;
123        GrPaint&&                    fPaint;
124        const GrUserStencilSettings* fUserStencilSettings;
125        GrRenderTargetContext*       fRenderTargetContext;
126        const GrClip*                fClip;
127        const SkMatrix*              fViewMatrix;
128        const GrShape*               fShape;
129        GrAAType                     fAAType;
130        bool                         fGammaCorrect;
131#ifdef SK_DEBUG
132        void validate() const {
133            SkASSERT(fContext);
134            SkASSERT(fUserStencilSettings);
135            SkASSERT(fRenderTargetContext);
136            SkASSERT(fClip);
137            SkASSERT(fViewMatrix);
138            SkASSERT(fShape);
139        }
140#endif
141    };
142
143    /**
144     * Draws the path into the draw target. If getStencilSupport() would return kNoRestriction then
145     * the subclass must respect the stencil settings.
146     */
147    bool drawPath(const DrawPathArgs& args) {
148        SkDEBUGCODE(args.validate();)
149#ifdef SK_DEBUG
150        CanDrawPathArgs canArgs;
151        canArgs.fCaps = args.fContext->caps();
152        canArgs.fViewMatrix = args.fViewMatrix;
153        canArgs.fShape = args.fShape;
154        canArgs.fAAType = args.fAAType;
155
156        canArgs.fHasUserStencilSettings = !args.fUserStencilSettings->isUnused();
157        SkASSERT(!(canArgs.fAAType == GrAAType::kMSAA &&
158                   GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType()));
159        SkASSERT(!(canArgs.fAAType == GrAAType::kMixedSamples &&
160                   GrFSAAType::kMixedSamples != args.fRenderTargetContext->fsaaType()));
161        SkASSERT(this->canDrawPath(canArgs));
162        if (!args.fUserStencilSettings->isUnused()) {
163            SkPath path;
164            args.fShape->asPath(&path);
165            SkASSERT(args.fShape->style().isSimpleFill());
166            SkASSERT(kNoRestriction_StencilSupport == this->getStencilSupport(*args.fShape));
167        }
168#endif
169        return this->onDrawPath(args);
170    }
171
172    /* Args to stencilPath().
173     *
174     * fResourceProvider      The resource provider for creating gpu resources to render the path
175     * fRenderTargetContext   The target of the draws
176     * fViewMatrix            Matrix applied to the path.
177     * fPath                  The path to draw.
178     * fAAType                The type of AA, cannot be kCoverage.
179     */
180    struct StencilPathArgs {
181        GrContext*             fContext;
182        GrRenderTargetContext* fRenderTargetContext;
183        const GrClip*          fClip;
184        const SkMatrix*        fViewMatrix;
185        GrAAType               fAAType;
186        const GrShape*         fShape;
187
188#ifdef SK_DEBUG
189        void validate() const {
190            SkASSERT(fContext);
191            SkASSERT(fRenderTargetContext);
192            SkASSERT(fViewMatrix);
193            SkASSERT(fShape);
194            SkASSERT(fShape->style().isSimpleFill());
195            SkASSERT(GrAAType::kCoverage != fAAType);
196            SkPath path;
197            fShape->asPath(&path);
198            SkASSERT(!path.isInverseFillType());
199        }
200#endif
201    };
202
203    /**
204     * Draws the path to the stencil buffer. Assume the writable stencil bits are already
205     * initialized to zero. The pixels inside the path will have non-zero stencil values afterwards.
206     */
207    void stencilPath(const StencilPathArgs& args) {
208        SkDEBUGCODE(args.validate();)
209        SkASSERT(kNoSupport_StencilSupport != this->getStencilSupport(*args.fShape));
210        this->onStencilPath(args);
211    }
212
213    // Helper for determining if we can treat a thin stroke as a hairline w/ coverage.
214    // If we can, we draw lots faster (raster device does this same test).
215    static bool IsStrokeHairlineOrEquivalent(const GrStyle& style, const SkMatrix& matrix,
216                                             SkScalar* outCoverage) {
217        if (style.pathEffect()) {
218            return false;
219        }
220        const SkStrokeRec& stroke = style.strokeRec();
221        if (stroke.isHairlineStyle()) {
222            if (outCoverage) {
223                *outCoverage = SK_Scalar1;
224            }
225            return true;
226        }
227        return stroke.getStyle() == SkStrokeRec::kStroke_Style &&
228            SkDrawTreatAAStrokeAsHairline(stroke.getWidth(), matrix, outCoverage);
229    }
230
231protected:
232    // Helper for getting the device bounds of a path. Inverse filled paths will have bounds set
233    // by devSize. Non-inverse path bounds will not necessarily be clipped to devSize.
234    static void GetPathDevBounds(const SkPath& path,
235                                 int devW,
236                                 int devH,
237                                 const SkMatrix& matrix,
238                                 SkRect* bounds);
239
240private:
241    /**
242     * Subclass overrides if it has any limitations of stenciling support.
243     */
244    virtual StencilSupport onGetStencilSupport(const GrShape&) const {
245        return kNoRestriction_StencilSupport;
246    }
247
248    /**
249     * Subclass implementation of drawPath()
250     */
251    virtual bool onDrawPath(const DrawPathArgs& args) = 0;
252
253    /**
254     * Subclass implementation of canDrawPath()
255     */
256    virtual bool onCanDrawPath(const CanDrawPathArgs& args) const = 0;
257
258    /**
259     * Subclass implementation of stencilPath(). Subclass must override iff it ever returns
260     * kStencilOnly in onGetStencilSupport().
261     */
262    virtual void onStencilPath(const StencilPathArgs& args) {
263        static constexpr GrUserStencilSettings kIncrementStencil(
264            GrUserStencilSettings::StaticInit<
265                 0xffff,
266                 GrUserStencilTest::kAlways,
267                 0xffff,
268                 GrUserStencilOp::kReplace,
269                 GrUserStencilOp::kReplace,
270                 0xffff>()
271        );
272
273        GrPaint paint;
274
275        DrawPathArgs drawArgs{args.fContext,
276                              std::move(paint),
277                              &kIncrementStencil,
278                              args.fRenderTargetContext,
279                              nullptr,  // clip
280                              args.fViewMatrix,
281                              args.fShape,
282                              args.fAAType,
283                              false};
284        this->drawPath(drawArgs);
285    }
286
287    typedef SkRefCnt INHERITED;
288};
289
290#endif
291