1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2006 The Android Open Source Project
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci
9cb93a386Sopenharmony_ci#include "include/core/SkPathMeasure.h"
10cb93a386Sopenharmony_ci#include "include/core/SkStrokeRec.h"
11cb93a386Sopenharmony_ci#include "include/effects/Sk1DPathEffect.h"
12cb93a386Sopenharmony_ci#include "src/core/SkPathEffectBase.h"
13cb93a386Sopenharmony_ci#include "src/core/SkReadBuffer.h"
14cb93a386Sopenharmony_ci#include "src/core/SkWriteBuffer.h"
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ci// Since we are stepping by a float, the do/while loop might go on forever (or nearly so).
17cb93a386Sopenharmony_ci// Put in a governor to limit crash values from looping too long (and allocating too much ram).
18cb93a386Sopenharmony_ci#define MAX_REASONABLE_ITERATIONS   100000
19cb93a386Sopenharmony_ci
20cb93a386Sopenharmony_ciclass Sk1DPathEffect : public SkPathEffectBase {
21cb93a386Sopenharmony_cipublic:
22cb93a386Sopenharmony_ciprotected:
23cb93a386Sopenharmony_ci    bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*,
24cb93a386Sopenharmony_ci                      const SkMatrix&) const override {
25cb93a386Sopenharmony_ci        SkPathMeasure   meas(src, false);
26cb93a386Sopenharmony_ci        do {
27cb93a386Sopenharmony_ci            int governor = MAX_REASONABLE_ITERATIONS;
28cb93a386Sopenharmony_ci            SkScalar    length = meas.getLength();
29cb93a386Sopenharmony_ci            SkScalar    distance = this->begin(length);
30cb93a386Sopenharmony_ci            while (distance < length && --governor >= 0) {
31cb93a386Sopenharmony_ci                SkScalar delta = this->next(dst, distance, meas);
32cb93a386Sopenharmony_ci                if (delta <= 0) {
33cb93a386Sopenharmony_ci                    break;
34cb93a386Sopenharmony_ci                }
35cb93a386Sopenharmony_ci                distance += delta;
36cb93a386Sopenharmony_ci            }
37cb93a386Sopenharmony_ci        } while (meas.nextContour());
38cb93a386Sopenharmony_ci        return true;
39cb93a386Sopenharmony_ci    }
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_ci    /** Called at the start of each contour, returns the initial offset
42cb93a386Sopenharmony_ci        into that contour.
43cb93a386Sopenharmony_ci    */
44cb93a386Sopenharmony_ci    virtual SkScalar begin(SkScalar contourLength) const = 0;
45cb93a386Sopenharmony_ci    /** Called with the current distance along the path, with the current matrix
46cb93a386Sopenharmony_ci        for the point/tangent at the specified distance.
47cb93a386Sopenharmony_ci        Return the distance to travel for the next call. If return <= 0, then that
48cb93a386Sopenharmony_ci        contour is done.
49cb93a386Sopenharmony_ci    */
50cb93a386Sopenharmony_ci    virtual SkScalar next(SkPath* dst, SkScalar dist, SkPathMeasure&) const = 0;
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ciprivate:
53cb93a386Sopenharmony_ci    // For simplicity, assume fast bounds cannot be computed
54cb93a386Sopenharmony_ci    bool computeFastBounds(SkRect*) const override { return false; }
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci    using INHERITED = SkPathEffect;
57cb93a386Sopenharmony_ci};
58cb93a386Sopenharmony_ci
59cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ciclass SkPath1DPathEffectImpl : public Sk1DPathEffect {
62cb93a386Sopenharmony_cipublic:
63cb93a386Sopenharmony_ci    SkPath1DPathEffectImpl(const SkPath& path, SkScalar advance, SkScalar phase,
64cb93a386Sopenharmony_ci                           SkPath1DPathEffect::Style style) : fPath(path) {
65cb93a386Sopenharmony_ci        SkASSERT(advance > 0 && !path.isEmpty());
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_ci        // Make the path thread-safe.
68cb93a386Sopenharmony_ci        fPath.updateBoundsCache();
69cb93a386Sopenharmony_ci        (void)fPath.getGenerationID();
70cb93a386Sopenharmony_ci
71cb93a386Sopenharmony_ci        // cleanup their phase parameter, inverting it so that it becomes an
72cb93a386Sopenharmony_ci        // offset along the path (to match the interpretation in PostScript)
73cb93a386Sopenharmony_ci        if (phase < 0) {
74cb93a386Sopenharmony_ci            phase = -phase;
75cb93a386Sopenharmony_ci            if (phase > advance) {
76cb93a386Sopenharmony_ci                phase = SkScalarMod(phase, advance);
77cb93a386Sopenharmony_ci            }
78cb93a386Sopenharmony_ci        } else {
79cb93a386Sopenharmony_ci            if (phase > advance) {
80cb93a386Sopenharmony_ci                phase = SkScalarMod(phase, advance);
81cb93a386Sopenharmony_ci            }
82cb93a386Sopenharmony_ci            phase = advance - phase;
83cb93a386Sopenharmony_ci        }
84cb93a386Sopenharmony_ci        // now catch the edge case where phase == advance (within epsilon)
85cb93a386Sopenharmony_ci        if (phase >= advance) {
86cb93a386Sopenharmony_ci            phase = 0;
87cb93a386Sopenharmony_ci        }
88cb93a386Sopenharmony_ci        SkASSERT(phase >= 0);
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci        fAdvance = advance;
91cb93a386Sopenharmony_ci        fInitialOffset = phase;
92cb93a386Sopenharmony_ci        fStyle = style;
93cb93a386Sopenharmony_ci    }
94cb93a386Sopenharmony_ci
95cb93a386Sopenharmony_ci    bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
96cb93a386Sopenharmony_ci                      const SkRect* cullRect, const SkMatrix& ctm) const override {
97cb93a386Sopenharmony_ci        rec->setFillStyle();
98cb93a386Sopenharmony_ci        return this->INHERITED::onFilterPath(dst, src, rec, cullRect, ctm);
99cb93a386Sopenharmony_ci    }
100cb93a386Sopenharmony_ci
101cb93a386Sopenharmony_ci    SkScalar begin(SkScalar contourLength) const override {
102cb93a386Sopenharmony_ci        return fInitialOffset;
103cb93a386Sopenharmony_ci    }
104cb93a386Sopenharmony_ci
105cb93a386Sopenharmony_ci    SkScalar next(SkPath*, SkScalar, SkPathMeasure&) const override;
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ci    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
108cb93a386Sopenharmony_ci        SkScalar advance = buffer.readScalar();
109cb93a386Sopenharmony_ci        SkPath path;
110cb93a386Sopenharmony_ci        buffer.readPath(&path);
111cb93a386Sopenharmony_ci        SkScalar phase = buffer.readScalar();
112cb93a386Sopenharmony_ci        SkPath1DPathEffect::Style style = buffer.read32LE(SkPath1DPathEffect::kLastEnum_Style);
113cb93a386Sopenharmony_ci        return buffer.isValid() ? SkPath1DPathEffect::Make(path, advance, phase, style) : nullptr;
114cb93a386Sopenharmony_ci    }
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci    void flatten(SkWriteBuffer& buffer) const override {
117cb93a386Sopenharmony_ci        buffer.writeScalar(fAdvance);
118cb93a386Sopenharmony_ci        buffer.writePath(fPath);
119cb93a386Sopenharmony_ci        buffer.writeScalar(fInitialOffset);
120cb93a386Sopenharmony_ci        buffer.writeUInt(fStyle);
121cb93a386Sopenharmony_ci    }
122cb93a386Sopenharmony_ci
123cb93a386Sopenharmony_ci    Factory getFactory() const override { return CreateProc; }
124cb93a386Sopenharmony_ci    const char* getTypeName() const override { return "SkPath1DPathEffectImpl"; }
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ciprivate:
127cb93a386Sopenharmony_ci    SkPath                      fPath;          // copied from constructor
128cb93a386Sopenharmony_ci    SkScalar                    fAdvance;       // copied from constructor
129cb93a386Sopenharmony_ci    SkScalar                    fInitialOffset; // computed from phase
130cb93a386Sopenharmony_ci    SkPath1DPathEffect::Style   fStyle;         // copied from constructor
131cb93a386Sopenharmony_ci
132cb93a386Sopenharmony_ci    using INHERITED = Sk1DPathEffect;
133cb93a386Sopenharmony_ci};
134cb93a386Sopenharmony_ci
135cb93a386Sopenharmony_cistatic bool morphpoints(SkPoint dst[], const SkPoint src[], int count,
136cb93a386Sopenharmony_ci                        SkPathMeasure& meas, SkScalar dist) {
137cb93a386Sopenharmony_ci    for (int i = 0; i < count; i++) {
138cb93a386Sopenharmony_ci        SkPoint pos;
139cb93a386Sopenharmony_ci        SkVector tangent;
140cb93a386Sopenharmony_ci
141cb93a386Sopenharmony_ci        SkScalar sx = src[i].fX;
142cb93a386Sopenharmony_ci        SkScalar sy = src[i].fY;
143cb93a386Sopenharmony_ci
144cb93a386Sopenharmony_ci        if (!meas.getPosTan(dist + sx, &pos, &tangent)) {
145cb93a386Sopenharmony_ci            return false;
146cb93a386Sopenharmony_ci        }
147cb93a386Sopenharmony_ci
148cb93a386Sopenharmony_ci        SkMatrix    matrix;
149cb93a386Sopenharmony_ci        SkPoint     pt;
150cb93a386Sopenharmony_ci
151cb93a386Sopenharmony_ci        pt.set(sx, sy);
152cb93a386Sopenharmony_ci        matrix.setSinCos(tangent.fY, tangent.fX, 0, 0);
153cb93a386Sopenharmony_ci        matrix.preTranslate(-sx, 0);
154cb93a386Sopenharmony_ci        matrix.postTranslate(pos.fX, pos.fY);
155cb93a386Sopenharmony_ci        matrix.mapPoints(&dst[i], &pt, 1);
156cb93a386Sopenharmony_ci    }
157cb93a386Sopenharmony_ci    return true;
158cb93a386Sopenharmony_ci}
159cb93a386Sopenharmony_ci
160cb93a386Sopenharmony_ci/*  TODO
161cb93a386Sopenharmony_ci
162cb93a386Sopenharmony_ciNeed differentially more subdivisions when the follow-path is curvy. Not sure how to
163cb93a386Sopenharmony_cidetermine that, but we need it. I guess a cheap answer is let the caller tell us,
164cb93a386Sopenharmony_cibut that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
165cb93a386Sopenharmony_ci*/
166cb93a386Sopenharmony_cistatic void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
167cb93a386Sopenharmony_ci                      SkScalar dist) {
168cb93a386Sopenharmony_ci    SkPath::Iter    iter(src, false);
169cb93a386Sopenharmony_ci    SkPoint         srcP[4], dstP[3];
170cb93a386Sopenharmony_ci    SkPath::Verb    verb;
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_ci    while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
173cb93a386Sopenharmony_ci        switch (verb) {
174cb93a386Sopenharmony_ci            case SkPath::kMove_Verb:
175cb93a386Sopenharmony_ci                if (morphpoints(dstP, srcP, 1, meas, dist)) {
176cb93a386Sopenharmony_ci                    dst->moveTo(dstP[0]);
177cb93a386Sopenharmony_ci                }
178cb93a386Sopenharmony_ci                break;
179cb93a386Sopenharmony_ci            case SkPath::kLine_Verb:
180cb93a386Sopenharmony_ci                srcP[2] = srcP[1];
181cb93a386Sopenharmony_ci                srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX),
182cb93a386Sopenharmony_ci                            SkScalarAve(srcP[0].fY, srcP[2].fY));
183cb93a386Sopenharmony_ci                [[fallthrough]];
184cb93a386Sopenharmony_ci            case SkPath::kQuad_Verb:
185cb93a386Sopenharmony_ci                if (morphpoints(dstP, &srcP[1], 2, meas, dist)) {
186cb93a386Sopenharmony_ci                    dst->quadTo(dstP[0], dstP[1]);
187cb93a386Sopenharmony_ci                }
188cb93a386Sopenharmony_ci                break;
189cb93a386Sopenharmony_ci            case SkPath::kConic_Verb:
190cb93a386Sopenharmony_ci                if (morphpoints(dstP, &srcP[1], 2, meas, dist)) {
191cb93a386Sopenharmony_ci                    dst->conicTo(dstP[0], dstP[1], iter.conicWeight());
192cb93a386Sopenharmony_ci                }
193cb93a386Sopenharmony_ci                break;
194cb93a386Sopenharmony_ci            case SkPath::kCubic_Verb:
195cb93a386Sopenharmony_ci                if (morphpoints(dstP, &srcP[1], 3, meas, dist)) {
196cb93a386Sopenharmony_ci                    dst->cubicTo(dstP[0], dstP[1], dstP[2]);
197cb93a386Sopenharmony_ci                }
198cb93a386Sopenharmony_ci                break;
199cb93a386Sopenharmony_ci            case SkPath::kClose_Verb:
200cb93a386Sopenharmony_ci                dst->close();
201cb93a386Sopenharmony_ci                break;
202cb93a386Sopenharmony_ci            default:
203cb93a386Sopenharmony_ci                SkDEBUGFAIL("unknown verb");
204cb93a386Sopenharmony_ci                break;
205cb93a386Sopenharmony_ci        }
206cb93a386Sopenharmony_ci    }
207cb93a386Sopenharmony_ci}
208cb93a386Sopenharmony_ci
209cb93a386Sopenharmony_ciSkScalar SkPath1DPathEffectImpl::next(SkPath* dst, SkScalar distance,
210cb93a386Sopenharmony_ci                                      SkPathMeasure& meas) const {
211cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_FUZZER)
212cb93a386Sopenharmony_ci    if (dst->countPoints() > 100000) {
213cb93a386Sopenharmony_ci        return fAdvance;
214cb93a386Sopenharmony_ci    }
215cb93a386Sopenharmony_ci#endif
216cb93a386Sopenharmony_ci    switch (fStyle) {
217cb93a386Sopenharmony_ci        case SkPath1DPathEffect::kTranslate_Style: {
218cb93a386Sopenharmony_ci            SkPoint pos;
219cb93a386Sopenharmony_ci            if (meas.getPosTan(distance, &pos, nullptr)) {
220cb93a386Sopenharmony_ci                dst->addPath(fPath, pos.fX, pos.fY);
221cb93a386Sopenharmony_ci            }
222cb93a386Sopenharmony_ci        } break;
223cb93a386Sopenharmony_ci        case SkPath1DPathEffect::kRotate_Style: {
224cb93a386Sopenharmony_ci            SkMatrix matrix;
225cb93a386Sopenharmony_ci            if (meas.getMatrix(distance, &matrix)) {
226cb93a386Sopenharmony_ci                dst->addPath(fPath, matrix);
227cb93a386Sopenharmony_ci            }
228cb93a386Sopenharmony_ci        } break;
229cb93a386Sopenharmony_ci        case SkPath1DPathEffect::kMorph_Style:
230cb93a386Sopenharmony_ci            morphpath(dst, fPath, meas, distance);
231cb93a386Sopenharmony_ci            break;
232cb93a386Sopenharmony_ci    }
233cb93a386Sopenharmony_ci    return fAdvance;
234cb93a386Sopenharmony_ci}
235cb93a386Sopenharmony_ci
236cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////////////////////////
237cb93a386Sopenharmony_ci
238cb93a386Sopenharmony_cisk_sp<SkPathEffect> SkPath1DPathEffect::Make(const SkPath& path, SkScalar advance, SkScalar phase,
239cb93a386Sopenharmony_ci                                             Style style) {
240cb93a386Sopenharmony_ci    if (advance <= 0 || !SkScalarIsFinite(advance) || !SkScalarIsFinite(phase) || path.isEmpty()) {
241cb93a386Sopenharmony_ci        return nullptr;
242cb93a386Sopenharmony_ci    }
243cb93a386Sopenharmony_ci    return sk_sp<SkPathEffect>(new SkPath1DPathEffectImpl(path, advance, phase, style));
244cb93a386Sopenharmony_ci}
245cb93a386Sopenharmony_ci
246cb93a386Sopenharmony_civoid SkPath1DPathEffect::RegisterFlattenables() {
247cb93a386Sopenharmony_ci    SK_REGISTER_FLATTENABLE(SkPath1DPathEffectImpl);
248cb93a386Sopenharmony_ci}
249