xref: /third_party/skia/src/core/SkPathEffect.cpp (revision cb93a386)
1/*
2 * Copyright 2006 The Android Open Source Project
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 "include/core/SkPath.h"
9#include "include/core/SkPathEffect.h"
10#include "src/core/SkPathEffectBase.h"
11#include "src/core/SkReadBuffer.h"
12#include "src/core/SkWriteBuffer.h"
13
14///////////////////////////////////////////////////////////////////////////////
15
16bool SkPathEffect::filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
17                              const SkRect* bounds) const {
18    return this->filterPath(dst, src, rec, bounds, SkMatrix::I());
19}
20
21bool SkPathEffect::filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
22                              const SkRect* bounds, const SkMatrix& ctm) const {
23    SkPath tmp, *tmpDst = dst;
24    if (dst == &src) {
25        tmpDst = &tmp;
26    }
27    if (as_PEB(this)->onFilterPath(tmpDst, src, rec, bounds, ctm)) {
28        if (dst == &src) {
29            *dst = tmp;
30        }
31        return true;
32    }
33    return false;
34}
35
36bool SkPathEffectBase::asPoints(PointData* results, const SkPath& src,
37                    const SkStrokeRec& rec, const SkMatrix& mx, const SkRect* rect) const {
38    return this->onAsPoints(results, src, rec, mx, rect);
39}
40
41SkPathEffect::DashType SkPathEffect::asADash(DashInfo* info) const {
42    return as_PEB(this)->onAsADash(info);
43}
44
45bool SkPathEffect::needsCTM() const {
46    return as_PEB(this)->onNeedsCTM();
47}
48
49///////////////////////////////////////////////////////////////////////////////
50
51/** \class SkPairPathEffect
52
53 Common baseclass for Compose and Sum. This subclass manages two pathEffects,
54 including flattening them. It does nothing in filterPath, and is only useful
55 for managing the lifetimes of its two arguments.
56 */
57class SkPairPathEffect : public SkPathEffectBase {
58protected:
59    SkPairPathEffect(sk_sp<SkPathEffect> pe0, sk_sp<SkPathEffect> pe1)
60        : fPE0(std::move(pe0)), fPE1(std::move(pe1))
61    {
62        SkASSERT(fPE0.get());
63        SkASSERT(fPE1.get());
64    }
65
66    void flatten(SkWriteBuffer& buffer) const override {
67        buffer.writeFlattenable(fPE0.get());
68        buffer.writeFlattenable(fPE1.get());
69    }
70
71    // these are visible to our subclasses
72    sk_sp<SkPathEffect> fPE0;
73    sk_sp<SkPathEffect> fPE1;
74
75private:
76    using INHERITED = SkPathEffectBase;
77};
78
79///////////////////////////////////////////////////////////////////////////////////////////////////
80
81class SkComposePathEffect : public SkPairPathEffect {
82public:
83    /** Construct a pathEffect whose effect is to apply first the inner pathEffect
84     and the the outer pathEffect (e.g. outer(inner(path)))
85     The reference counts for outer and inner are both incremented in the constructor,
86     and decremented in the destructor.
87     */
88    static sk_sp<SkPathEffect> Make(sk_sp<SkPathEffect> outer, sk_sp<SkPathEffect> inner) {
89        if (!outer) {
90            return inner;
91        }
92        if (!inner) {
93            return outer;
94        }
95        return sk_sp<SkPathEffect>(new SkComposePathEffect(outer, inner));
96    }
97
98    SkComposePathEffect(sk_sp<SkPathEffect> outer, sk_sp<SkPathEffect> inner)
99        : INHERITED(outer, inner) {}
100
101    bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
102                       const SkRect* cullRect, const SkMatrix& ctm) const override {
103        SkPath          tmp;
104        const SkPath*   ptr = &src;
105
106        if (fPE1->filterPath(&tmp, src, rec, cullRect, ctm)) {
107            ptr = &tmp;
108        }
109        return fPE0->filterPath(dst, *ptr, rec, cullRect, ctm);
110    }
111
112    SK_FLATTENABLE_HOOKS(SkComposePathEffect)
113
114    bool computeFastBounds(SkRect* bounds) const override {
115        // inner (fPE1) is computed first, automatically updating bounds before computing outer.
116        return as_PEB(fPE1)->computeFastBounds(bounds) &&
117               as_PEB(fPE0)->computeFastBounds(bounds);
118    }
119
120private:
121    // illegal
122    SkComposePathEffect(const SkComposePathEffect&);
123    SkComposePathEffect& operator=(const SkComposePathEffect&);
124    friend class SkPathEffect;
125
126    using INHERITED = SkPairPathEffect;
127};
128
129sk_sp<SkFlattenable> SkComposePathEffect::CreateProc(SkReadBuffer& buffer) {
130    sk_sp<SkPathEffect> pe0(buffer.readPathEffect());
131    sk_sp<SkPathEffect> pe1(buffer.readPathEffect());
132    return SkComposePathEffect::Make(std::move(pe0), std::move(pe1));
133}
134
135///////////////////////////////////////////////////////////////////////////////
136
137/** \class SkSumPathEffect
138
139 This subclass of SkPathEffect applies two pathEffects, one after the other.
140 Its filterPath() returns true if either of the effects succeeded.
141 */
142class SkSumPathEffect : public SkPairPathEffect {
143public:
144    /** Construct a pathEffect whose effect is to apply two effects, in sequence.
145     (e.g. first(path) + second(path))
146     The reference counts for first and second are both incremented in the constructor,
147     and decremented in the destructor.
148     */
149    static sk_sp<SkPathEffect> Make(sk_sp<SkPathEffect> first, sk_sp<SkPathEffect> second) {
150        if (!first) {
151            return second;
152        }
153        if (!second) {
154            return first;
155        }
156        return sk_sp<SkPathEffect>(new SkSumPathEffect(first, second));
157    }
158
159    SkSumPathEffect(sk_sp<SkPathEffect> first, sk_sp<SkPathEffect> second)
160        : INHERITED(first, second) {}
161
162    bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
163                      const SkRect* cullRect, const SkMatrix& ctm) const override {
164        // always call both, even if the first one succeeds
165        bool filteredFirst = fPE0->filterPath(dst, src, rec, cullRect, ctm);
166        bool filteredSecond = fPE1->filterPath(dst, src, rec, cullRect, ctm);
167        return filteredFirst || filteredSecond;
168    }
169
170    SK_FLATTENABLE_HOOKS(SkSumPathEffect)
171
172    bool computeFastBounds(SkRect* bounds) const override {
173        // Unlike Compose(), PE0 modifies the path first for Sum
174        return as_PEB(fPE0)->computeFastBounds(bounds) &&
175               as_PEB(fPE1)->computeFastBounds(bounds);
176    }
177
178private:
179    // illegal
180    SkSumPathEffect(const SkSumPathEffect&);
181    SkSumPathEffect& operator=(const SkSumPathEffect&);
182    friend class SkPathEffect;
183
184    using INHERITED = SkPairPathEffect;
185};
186
187sk_sp<SkFlattenable> SkSumPathEffect::CreateProc(SkReadBuffer& buffer) {
188    sk_sp<SkPathEffect> pe0(buffer.readPathEffect());
189    sk_sp<SkPathEffect> pe1(buffer.readPathEffect());
190    return SkSumPathEffect::Make(pe0, pe1);
191}
192
193///////////////////////////////////////////////////////////////////////////////////////////////////
194
195sk_sp<SkPathEffect> SkPathEffect::MakeSum(sk_sp<SkPathEffect> first, sk_sp<SkPathEffect> second) {
196    return SkSumPathEffect::Make(std::move(first), std::move(second));
197}
198
199sk_sp<SkPathEffect> SkPathEffect::MakeCompose(sk_sp<SkPathEffect> outer,
200                                              sk_sp<SkPathEffect> inner) {
201    return SkComposePathEffect::Make(std::move(outer), std::move(inner));
202}
203
204void SkPathEffectBase::RegisterFlattenables() {
205    SK_REGISTER_FLATTENABLE(SkComposePathEffect);
206    SK_REGISTER_FLATTENABLE(SkSumPathEffect);
207}
208
209sk_sp<SkPathEffect> SkPathEffect::Deserialize(const void* data, size_t size,
210                                              const SkDeserialProcs* procs) {
211    return sk_sp<SkPathEffect>(static_cast<SkPathEffect*>(
212                               SkFlattenable::Deserialize(
213                               kSkPathEffect_Type, data, size, procs).release()));
214}
215