xref: /aosp_15_r20/external/skia/src/effects/Sk2DPathEffect.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2006 The Android Open Source Project
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/Sk2DPathEffect.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFlattenable.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathEffect.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRegion.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStrokeRec.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPathEffectBase.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkReadBuffer.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkWriteBuffer.h"
24*c8dee2aaSAndroid Build Coastguard Worker 
25*c8dee2aaSAndroid Build Coastguard Worker class Sk2DPathEffect : public SkPathEffectBase {
26*c8dee2aaSAndroid Build Coastguard Worker public:
Sk2DPathEffect(const SkMatrix & mat)27*c8dee2aaSAndroid Build Coastguard Worker     Sk2DPathEffect(const SkMatrix& mat) : fMatrix(mat) {
28*c8dee2aaSAndroid Build Coastguard Worker         // Calling invert will set the type mask on both matrices, making them thread safe.
29*c8dee2aaSAndroid Build Coastguard Worker         fMatrixIsInvertible = fMatrix.invert(&fInverse);
30*c8dee2aaSAndroid Build Coastguard Worker     }
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker protected:
33*c8dee2aaSAndroid Build Coastguard Worker     /** New virtual, to be overridden by subclasses.
34*c8dee2aaSAndroid Build Coastguard Worker         This is called once from filterPath, and provides the
35*c8dee2aaSAndroid Build Coastguard Worker         uv parameter bounds for the path. Subsequent calls to
36*c8dee2aaSAndroid Build Coastguard Worker         next() will receive u and v values within these bounds,
37*c8dee2aaSAndroid Build Coastguard Worker         and then a call to end() will signal the end of processing.
38*c8dee2aaSAndroid Build Coastguard Worker     */
begin(const SkIRect & uvBounds,SkPath * dst) const39*c8dee2aaSAndroid Build Coastguard Worker     virtual void begin(const SkIRect& uvBounds, SkPath* dst) const {}
next(const SkPoint & loc,int u,int v,SkPath * dst) const40*c8dee2aaSAndroid Build Coastguard Worker     virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) const {}
end(SkPath * dst) const41*c8dee2aaSAndroid Build Coastguard Worker     virtual void end(SkPath* dst) const {}
42*c8dee2aaSAndroid Build Coastguard Worker 
43*c8dee2aaSAndroid Build Coastguard Worker     /** Low-level virtual called per span of locations in the u-direction.
44*c8dee2aaSAndroid Build Coastguard Worker         The default implementation calls next() repeatedly with each
45*c8dee2aaSAndroid Build Coastguard Worker         location.
46*c8dee2aaSAndroid Build Coastguard Worker     */
nextSpan(int x,int y,int ucount,SkPath * path) const47*c8dee2aaSAndroid Build Coastguard Worker     virtual void nextSpan(int x, int y, int ucount, SkPath* path) const {
48*c8dee2aaSAndroid Build Coastguard Worker         if (!fMatrixIsInvertible) {
49*c8dee2aaSAndroid Build Coastguard Worker             return;
50*c8dee2aaSAndroid Build Coastguard Worker         }
51*c8dee2aaSAndroid Build Coastguard Worker     #if defined(SK_BUILD_FOR_FUZZER)
52*c8dee2aaSAndroid Build Coastguard Worker         if (ucount > 100) {
53*c8dee2aaSAndroid Build Coastguard Worker             return;
54*c8dee2aaSAndroid Build Coastguard Worker         }
55*c8dee2aaSAndroid Build Coastguard Worker     #endif
56*c8dee2aaSAndroid Build Coastguard Worker 
57*c8dee2aaSAndroid Build Coastguard Worker         const SkMatrix& mat = this->getMatrix();
58*c8dee2aaSAndroid Build Coastguard Worker         SkPoint src, dst;
59*c8dee2aaSAndroid Build Coastguard Worker 
60*c8dee2aaSAndroid Build Coastguard Worker         src.set(SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf);
61*c8dee2aaSAndroid Build Coastguard Worker         do {
62*c8dee2aaSAndroid Build Coastguard Worker             mat.mapPoints(&dst, &src, 1);
63*c8dee2aaSAndroid Build Coastguard Worker             this->next(dst, x++, y, path);
64*c8dee2aaSAndroid Build Coastguard Worker             src.fX += SK_Scalar1;
65*c8dee2aaSAndroid Build Coastguard Worker         } while (--ucount > 0);
66*c8dee2aaSAndroid Build Coastguard Worker     }
67*c8dee2aaSAndroid Build Coastguard Worker 
getMatrix() const68*c8dee2aaSAndroid Build Coastguard Worker     const SkMatrix& getMatrix() const { return fMatrix; }
69*c8dee2aaSAndroid Build Coastguard Worker 
flatten(SkWriteBuffer & buffer) const70*c8dee2aaSAndroid Build Coastguard Worker     void flatten(SkWriteBuffer& buffer) const override {
71*c8dee2aaSAndroid Build Coastguard Worker         buffer.writeMatrix(fMatrix);
72*c8dee2aaSAndroid Build Coastguard Worker     }
73*c8dee2aaSAndroid Build Coastguard Worker 
onFilterPath(SkPath * dst,const SkPath & src,SkStrokeRec * rec,const SkRect * cullRect,const SkMatrix &) const74*c8dee2aaSAndroid Build Coastguard Worker     bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
75*c8dee2aaSAndroid Build Coastguard Worker                       const SkRect* cullRect, const SkMatrix&) const override {
76*c8dee2aaSAndroid Build Coastguard Worker         if (!fMatrixIsInvertible) {
77*c8dee2aaSAndroid Build Coastguard Worker             return false;
78*c8dee2aaSAndroid Build Coastguard Worker         }
79*c8dee2aaSAndroid Build Coastguard Worker 
80*c8dee2aaSAndroid Build Coastguard Worker         SkPath  tmp;
81*c8dee2aaSAndroid Build Coastguard Worker         SkIRect ir;
82*c8dee2aaSAndroid Build Coastguard Worker 
83*c8dee2aaSAndroid Build Coastguard Worker         src.transform(fInverse, &tmp);
84*c8dee2aaSAndroid Build Coastguard Worker         tmp.getBounds().round(&ir);
85*c8dee2aaSAndroid Build Coastguard Worker         if (!ir.isEmpty()) {
86*c8dee2aaSAndroid Build Coastguard Worker             this->begin(ir, dst);
87*c8dee2aaSAndroid Build Coastguard Worker 
88*c8dee2aaSAndroid Build Coastguard Worker             SkRegion rgn;
89*c8dee2aaSAndroid Build Coastguard Worker             rgn.setPath(tmp, SkRegion(ir));
90*c8dee2aaSAndroid Build Coastguard Worker             SkRegion::Iterator iter(rgn);
91*c8dee2aaSAndroid Build Coastguard Worker             for (; !iter.done(); iter.next()) {
92*c8dee2aaSAndroid Build Coastguard Worker                 const SkIRect& rect = iter.rect();
93*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_BUILD_FOR_FUZZER)
94*c8dee2aaSAndroid Build Coastguard Worker                 if (rect.height() > 100) {
95*c8dee2aaSAndroid Build Coastguard Worker                     continue;
96*c8dee2aaSAndroid Build Coastguard Worker                 }
97*c8dee2aaSAndroid Build Coastguard Worker #endif
98*c8dee2aaSAndroid Build Coastguard Worker                 for (int y = rect.fTop; y < rect.fBottom; ++y) {
99*c8dee2aaSAndroid Build Coastguard Worker                     this->nextSpan(rect.fLeft, y, rect.width(), dst);
100*c8dee2aaSAndroid Build Coastguard Worker                 }
101*c8dee2aaSAndroid Build Coastguard Worker             }
102*c8dee2aaSAndroid Build Coastguard Worker 
103*c8dee2aaSAndroid Build Coastguard Worker             this->end(dst);
104*c8dee2aaSAndroid Build Coastguard Worker         }
105*c8dee2aaSAndroid Build Coastguard Worker         return true;
106*c8dee2aaSAndroid Build Coastguard Worker     }
107*c8dee2aaSAndroid Build Coastguard Worker 
108*c8dee2aaSAndroid Build Coastguard Worker private:
109*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix    fMatrix, fInverse;
110*c8dee2aaSAndroid Build Coastguard Worker     bool        fMatrixIsInvertible;
111*c8dee2aaSAndroid Build Coastguard Worker 
112*c8dee2aaSAndroid Build Coastguard Worker     // For simplicity, assume fast bounds cannot be computed
computeFastBounds(SkRect *) const113*c8dee2aaSAndroid Build Coastguard Worker     bool computeFastBounds(SkRect*) const override { return false; }
114*c8dee2aaSAndroid Build Coastguard Worker 
115*c8dee2aaSAndroid Build Coastguard Worker     friend class Sk2DPathEffectBlitter;
116*c8dee2aaSAndroid Build Coastguard Worker };
117*c8dee2aaSAndroid Build Coastguard Worker 
118*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
119*c8dee2aaSAndroid Build Coastguard Worker 
120*c8dee2aaSAndroid Build Coastguard Worker class SkLine2DPathEffectImpl : public Sk2DPathEffect {
121*c8dee2aaSAndroid Build Coastguard Worker public:
SkLine2DPathEffectImpl(SkScalar width,const SkMatrix & matrix)122*c8dee2aaSAndroid Build Coastguard Worker     SkLine2DPathEffectImpl(SkScalar width, const SkMatrix& matrix)
123*c8dee2aaSAndroid Build Coastguard Worker         : Sk2DPathEffect(matrix)
124*c8dee2aaSAndroid Build Coastguard Worker         , fWidth(width)
125*c8dee2aaSAndroid Build Coastguard Worker     {
126*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(width >= 0);
127*c8dee2aaSAndroid Build Coastguard Worker     }
128*c8dee2aaSAndroid Build Coastguard Worker 
onFilterPath(SkPath * dst,const SkPath & src,SkStrokeRec * rec,const SkRect * cullRect,const SkMatrix & ctm) const129*c8dee2aaSAndroid Build Coastguard Worker     bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
130*c8dee2aaSAndroid Build Coastguard Worker                       const SkRect* cullRect, const SkMatrix& ctm) const override {
131*c8dee2aaSAndroid Build Coastguard Worker         if (this->INHERITED::onFilterPath(dst, src, rec, cullRect, ctm)) {
132*c8dee2aaSAndroid Build Coastguard Worker             rec->setStrokeStyle(fWidth);
133*c8dee2aaSAndroid Build Coastguard Worker             return true;
134*c8dee2aaSAndroid Build Coastguard Worker         }
135*c8dee2aaSAndroid Build Coastguard Worker         return false;
136*c8dee2aaSAndroid Build Coastguard Worker     }
137*c8dee2aaSAndroid Build Coastguard Worker 
nextSpan(int u,int v,int ucount,SkPath * dst) const138*c8dee2aaSAndroid Build Coastguard Worker     void nextSpan(int u, int v, int ucount, SkPath* dst) const override {
139*c8dee2aaSAndroid Build Coastguard Worker         if (ucount > 1) {
140*c8dee2aaSAndroid Build Coastguard Worker             SkPoint    src[2], dstP[2];
141*c8dee2aaSAndroid Build Coastguard Worker 
142*c8dee2aaSAndroid Build Coastguard Worker             src[0].set(SkIntToScalar(u) + SK_ScalarHalf, SkIntToScalar(v) + SK_ScalarHalf);
143*c8dee2aaSAndroid Build Coastguard Worker             src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf, SkIntToScalar(v) + SK_ScalarHalf);
144*c8dee2aaSAndroid Build Coastguard Worker             this->getMatrix().mapPoints(dstP, src, 2);
145*c8dee2aaSAndroid Build Coastguard Worker 
146*c8dee2aaSAndroid Build Coastguard Worker             dst->moveTo(dstP[0]);
147*c8dee2aaSAndroid Build Coastguard Worker             dst->lineTo(dstP[1]);
148*c8dee2aaSAndroid Build Coastguard Worker         }
149*c8dee2aaSAndroid Build Coastguard Worker     }
150*c8dee2aaSAndroid Build Coastguard Worker 
CreateProc(SkReadBuffer & buffer)151*c8dee2aaSAndroid Build Coastguard Worker     static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
152*c8dee2aaSAndroid Build Coastguard Worker         SkMatrix matrix;
153*c8dee2aaSAndroid Build Coastguard Worker         buffer.readMatrix(&matrix);
154*c8dee2aaSAndroid Build Coastguard Worker         SkScalar width = buffer.readScalar();
155*c8dee2aaSAndroid Build Coastguard Worker         return SkLine2DPathEffect::Make(width, matrix);
156*c8dee2aaSAndroid Build Coastguard Worker     }
157*c8dee2aaSAndroid Build Coastguard Worker 
flatten(SkWriteBuffer & buffer) const158*c8dee2aaSAndroid Build Coastguard Worker     void flatten(SkWriteBuffer &buffer) const override {
159*c8dee2aaSAndroid Build Coastguard Worker         buffer.writeMatrix(this->getMatrix());
160*c8dee2aaSAndroid Build Coastguard Worker         buffer.writeScalar(fWidth);
161*c8dee2aaSAndroid Build Coastguard Worker     }
162*c8dee2aaSAndroid Build Coastguard Worker 
getFactory() const163*c8dee2aaSAndroid Build Coastguard Worker     Factory getFactory() const override { return CreateProc; }
getTypeName() const164*c8dee2aaSAndroid Build Coastguard Worker     const char* getTypeName() const override { return "SkLine2DPathEffect"; }
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker private:
167*c8dee2aaSAndroid Build Coastguard Worker     SkScalar fWidth;
168*c8dee2aaSAndroid Build Coastguard Worker 
169*c8dee2aaSAndroid Build Coastguard Worker     using INHERITED = Sk2DPathEffect;
170*c8dee2aaSAndroid Build Coastguard Worker };
171*c8dee2aaSAndroid Build Coastguard Worker 
172*c8dee2aaSAndroid Build Coastguard Worker /////////////////////////////////////////////////////////////////////////////////////////////////
173*c8dee2aaSAndroid Build Coastguard Worker 
174*c8dee2aaSAndroid Build Coastguard Worker class SkPath2DPathEffectImpl : public Sk2DPathEffect {
175*c8dee2aaSAndroid Build Coastguard Worker public:
SkPath2DPathEffectImpl(const SkMatrix & m,const SkPath & p)176*c8dee2aaSAndroid Build Coastguard Worker     SkPath2DPathEffectImpl(const SkMatrix& m, const SkPath& p) : INHERITED(m), fPath(p) {}
177*c8dee2aaSAndroid Build Coastguard Worker 
next(const SkPoint & loc,int u,int v,SkPath * dst) const178*c8dee2aaSAndroid Build Coastguard Worker     void next(const SkPoint& loc, int u, int v, SkPath* dst) const override {
179*c8dee2aaSAndroid Build Coastguard Worker         dst->addPath(fPath, loc.fX, loc.fY);
180*c8dee2aaSAndroid Build Coastguard Worker     }
181*c8dee2aaSAndroid Build Coastguard Worker 
CreateProc(SkReadBuffer & buffer)182*c8dee2aaSAndroid Build Coastguard Worker     static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
183*c8dee2aaSAndroid Build Coastguard Worker         SkMatrix matrix;
184*c8dee2aaSAndroid Build Coastguard Worker         buffer.readMatrix(&matrix);
185*c8dee2aaSAndroid Build Coastguard Worker         SkPath path;
186*c8dee2aaSAndroid Build Coastguard Worker         buffer.readPath(&path);
187*c8dee2aaSAndroid Build Coastguard Worker         return SkPath2DPathEffect::Make(matrix, path);
188*c8dee2aaSAndroid Build Coastguard Worker     }
189*c8dee2aaSAndroid Build Coastguard Worker 
flatten(SkWriteBuffer & buffer) const190*c8dee2aaSAndroid Build Coastguard Worker     void flatten(SkWriteBuffer& buffer) const override {
191*c8dee2aaSAndroid Build Coastguard Worker         buffer.writeMatrix(this->getMatrix());
192*c8dee2aaSAndroid Build Coastguard Worker         buffer.writePath(fPath);
193*c8dee2aaSAndroid Build Coastguard Worker     }
194*c8dee2aaSAndroid Build Coastguard Worker 
getFactory() const195*c8dee2aaSAndroid Build Coastguard Worker     Factory getFactory() const override { return CreateProc; }
getTypeName() const196*c8dee2aaSAndroid Build Coastguard Worker     const char* getTypeName() const override { return "SkPath2DPathEffect"; }
197*c8dee2aaSAndroid Build Coastguard Worker 
198*c8dee2aaSAndroid Build Coastguard Worker private:
199*c8dee2aaSAndroid Build Coastguard Worker     SkPath  fPath;
200*c8dee2aaSAndroid Build Coastguard Worker 
201*c8dee2aaSAndroid Build Coastguard Worker     using INHERITED = Sk2DPathEffect;
202*c8dee2aaSAndroid Build Coastguard Worker };
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker //////////////////////////////////////////////////////////////////////////////////////////////////
205*c8dee2aaSAndroid Build Coastguard Worker 
Make(SkScalar width,const SkMatrix & matrix)206*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPathEffect> SkLine2DPathEffect::Make(SkScalar width, const SkMatrix& matrix) {
207*c8dee2aaSAndroid Build Coastguard Worker     if (!(width >= 0)) {
208*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
209*c8dee2aaSAndroid Build Coastguard Worker     }
210*c8dee2aaSAndroid Build Coastguard Worker     return sk_sp<SkPathEffect>(new SkLine2DPathEffectImpl(width, matrix));
211*c8dee2aaSAndroid Build Coastguard Worker }
212*c8dee2aaSAndroid Build Coastguard Worker 
Make(const SkMatrix & matrix,const SkPath & path)213*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPathEffect> SkPath2DPathEffect::Make(const SkMatrix& matrix, const SkPath& path) {
214*c8dee2aaSAndroid Build Coastguard Worker     return sk_sp<SkPathEffect>(new SkPath2DPathEffectImpl(matrix, path));
215*c8dee2aaSAndroid Build Coastguard Worker }
216*c8dee2aaSAndroid Build Coastguard Worker 
RegisterFlattenables()217*c8dee2aaSAndroid Build Coastguard Worker void SkLine2DPathEffect::RegisterFlattenables() {
218*c8dee2aaSAndroid Build Coastguard Worker     SK_REGISTER_FLATTENABLE(SkLine2DPathEffectImpl);
219*c8dee2aaSAndroid Build Coastguard Worker }
220*c8dee2aaSAndroid Build Coastguard Worker 
RegisterFlattenables()221*c8dee2aaSAndroid Build Coastguard Worker void SkPath2DPathEffect::RegisterFlattenables() {
222*c8dee2aaSAndroid Build Coastguard Worker     SK_REGISTER_FLATTENABLE(SkPath2DPathEffectImpl);
223*c8dee2aaSAndroid Build Coastguard Worker }
224