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