1
2/*
3 * Copyright 2006 The Android Open Source Project
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#ifndef SkPathEffect_DEFINED
11#define SkPathEffect_DEFINED
12
13#include "SkFlattenable.h"
14#include "SkPath.h"
15#include "SkPoint.h"
16#include "SkRect.h"
17
18class SkPath;
19class SkStrokeRec;
20
21/** \class SkPathEffect
22
23    SkPathEffect is the base class for objects in the SkPaint that affect
24    the geometry of a drawing primitive before it is transformed by the
25    canvas' matrix and drawn.
26
27    Dashing is implemented as a subclass of SkPathEffect.
28*/
29class SK_API SkPathEffect : public SkFlattenable {
30public:
31    /**
32     *  Given a src path (input) and a stroke-rec (input and output), apply
33     *  this effect to the src path, returning the new path in dst, and return
34     *  true. If this effect cannot be applied, return false and ignore dst
35     *  and stroke-rec.
36     *
37     *  The stroke-rec specifies the initial request for stroking (if any).
38     *  The effect can treat this as input only, or it can choose to change
39     *  the rec as well. For example, the effect can decide to change the
40     *  stroke's width or join, or the effect can change the rec from stroke
41     *  to fill (or fill to stroke) in addition to returning a new (dst) path.
42     *
43     *  If this method returns true, the caller will apply (as needed) the
44     *  resulting stroke-rec to dst and then draw.
45     */
46    virtual bool filterPath(SkPath* dst, const SkPath& src,
47                            SkStrokeRec*, const SkRect* cullR) const = 0;
48
49    /**
50     *  Compute a conservative bounds for its effect, given the src bounds.
51     *  The baseline implementation just assigns src to dst.
52     */
53    virtual void computeFastBounds(SkRect* dst, const SkRect& src) const;
54
55    /** \class PointData
56
57        PointData aggregates all the information needed to draw the point
58        primitives returned by an 'asPoints' call.
59    */
60    class PointData {
61    public:
62        PointData()
63            : fFlags(0)
64            , fPoints(NULL)
65            , fNumPoints(0) {
66            fSize.set(SK_Scalar1, SK_Scalar1);
67            // 'asPoints' needs to initialize/fill-in 'fClipRect' if it sets
68            // the kUseClip flag
69        };
70        ~PointData() {
71            delete [] fPoints;
72        }
73
74        // TODO: consider using passed-in flags to limit the work asPoints does.
75        // For example, a kNoPath flag could indicate don't bother generating
76        // stamped solutions.
77
78        // Currently none of these flags are supported.
79        enum PointFlags {
80            kCircles_PointFlag            = 0x01,   // draw points as circles (instead of rects)
81            kUsePath_PointFlag            = 0x02,   // draw points as stamps of the returned path
82            kUseClip_PointFlag            = 0x04,   // apply 'fClipRect' before drawing the points
83        };
84
85        uint32_t           fFlags;      // flags that impact the drawing of the points
86        SkPoint*           fPoints;     // the center point of each generated point
87        int                fNumPoints;  // number of points in fPoints
88        SkVector           fSize;       // the size to draw the points
89        SkRect             fClipRect;   // clip required to draw the points (if kUseClip is set)
90        SkPath             fPath;       // 'stamp' to be used at each point (if kUsePath is set)
91
92        SkPath             fFirst;      // If not empty, contains geometry for first point
93        SkPath             fLast;       // If not empty, contains geometry for last point
94    };
95
96    /**
97     *  Does applying this path effect to 'src' yield a set of points? If so,
98     *  optionally return the points in 'results'.
99     */
100    virtual bool asPoints(PointData* results, const SkPath& src,
101                          const SkStrokeRec&, const SkMatrix&,
102                          const SkRect* cullR) const;
103
104    /**
105     *  If the PathEffect can be represented as a dash pattern, asADash will return kDash_DashType
106     *  and None otherwise. If a non NULL info is passed in, the various DashInfo will be filled
107     *  in if the PathEffect can be a dash pattern. If passed in info has an fCount equal or
108     *  greater to that of the effect, it will memcpy the values of the dash intervals into the
109     *  info. Thus the general approach will be call asADash once with default info to get DashType
110     *  and fCount. If effect can be represented as a dash pattern, allocate space for the intervals
111     *  in info, then call asADash again with the same info and the intervals will get copied in.
112     */
113
114    enum DashType {
115        kNone_DashType, //!< ignores the info parameter
116        kDash_DashType, //!< fills in all of the info parameter
117    };
118
119    struct DashInfo {
120        DashInfo() : fIntervals(NULL), fCount(0), fPhase(0) {}
121
122        SkScalar*   fIntervals;         //!< Length of on/off intervals for dashed lines
123                                        //   Even values represent ons, and odds offs
124        int32_t     fCount;             //!< Number of intervals in the dash. Should be even number
125        SkScalar    fPhase;             //!< Offset into the dashed interval pattern
126                                        //   mod the sum of all intervals
127    };
128
129    virtual DashType asADash(DashInfo* info) const;
130
131    SK_TO_STRING_PUREVIRT()
132    SK_DEFINE_FLATTENABLE_TYPE(SkPathEffect)
133
134#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
135    /// Override for subclasses as appropriate.
136    virtual bool exposedInAndroidJavaAPI() const { return false; }
137#endif
138
139protected:
140    SkPathEffect() {}
141
142private:
143    // illegal
144    SkPathEffect(const SkPathEffect&);
145    SkPathEffect& operator=(const SkPathEffect&);
146
147    typedef SkFlattenable INHERITED;
148};
149
150/** \class SkPairPathEffect
151
152    Common baseclass for Compose and Sum. This subclass manages two pathEffects,
153    including flattening them. It does nothing in filterPath, and is only useful
154    for managing the lifetimes of its two arguments.
155*/
156class SkPairPathEffect : public SkPathEffect {
157public:
158    virtual ~SkPairPathEffect();
159
160protected:
161    SkPairPathEffect(SkPathEffect* pe0, SkPathEffect* pe1);
162
163    void flatten(SkWriteBuffer&) const override;
164
165    // these are visible to our subclasses
166    SkPathEffect* fPE0, *fPE1;
167
168    SK_TO_STRING_OVERRIDE()
169
170private:
171    typedef SkPathEffect INHERITED;
172};
173
174/** \class SkComposePathEffect
175
176    This subclass of SkPathEffect composes its two arguments, to create
177    a compound pathEffect.
178*/
179class SkComposePathEffect : public SkPairPathEffect {
180public:
181    /** Construct a pathEffect whose effect is to apply first the inner pathEffect
182        and the the outer pathEffect (e.g. outer(inner(path)))
183        The reference counts for outer and inner are both incremented in the constructor,
184        and decremented in the destructor.
185    */
186    static SkPathEffect* Create(SkPathEffect* outer, SkPathEffect* inner) {
187        if (!outer) {
188            return SkSafeRef(inner);
189        }
190        if (!inner) {
191            return SkSafeRef(outer);
192        }
193        return new SkComposePathEffect(outer, inner);
194    }
195
196    virtual bool filterPath(SkPath* dst, const SkPath& src,
197                            SkStrokeRec*, const SkRect*) const override;
198
199    SK_TO_STRING_OVERRIDE()
200    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposePathEffect)
201
202#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
203    bool exposedInAndroidJavaAPI() const override { return true; }
204#endif
205
206protected:
207    SkComposePathEffect(SkPathEffect* outer, SkPathEffect* inner) : INHERITED(outer, inner) {}
208
209private:
210    // illegal
211    SkComposePathEffect(const SkComposePathEffect&);
212    SkComposePathEffect& operator=(const SkComposePathEffect&);
213
214    typedef SkPairPathEffect INHERITED;
215};
216
217/** \class SkSumPathEffect
218
219    This subclass of SkPathEffect applies two pathEffects, one after the other.
220    Its filterPath() returns true if either of the effects succeeded.
221*/
222class SkSumPathEffect : public SkPairPathEffect {
223public:
224    /** Construct a pathEffect whose effect is to apply two effects, in sequence.
225        (e.g. first(path) + second(path))
226        The reference counts for first and second are both incremented in the constructor,
227        and decremented in the destructor.
228    */
229    static SkPathEffect* Create(SkPathEffect* first, SkPathEffect* second) {
230        if (!first) {
231            return SkSafeRef(second);
232        }
233        if (!second) {
234            return SkSafeRef(first);
235        }
236        return new SkSumPathEffect(first, second);
237    }
238
239    virtual bool filterPath(SkPath* dst, const SkPath& src,
240                            SkStrokeRec*, const SkRect*) const override;
241
242    SK_TO_STRING_OVERRIDE()
243    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSumPathEffect)
244
245#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
246    bool exposedInAndroidJavaAPI() const override { return true; }
247#endif
248
249protected:
250    SkSumPathEffect(SkPathEffect* first, SkPathEffect* second) : INHERITED(first, second) {}
251
252private:
253    // illegal
254    SkSumPathEffect(const SkSumPathEffect&);
255    SkSumPathEffect& operator=(const SkSumPathEffect&);
256
257    typedef SkPairPathEffect INHERITED;
258};
259
260#endif
261