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