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
9#include "SkDiscretePathEffect.h"
10#include "SkFixed.h"
11#include "SkPathMeasure.h"
12#include "SkReadBuffer.h"
13#include "SkStrokeRec.h"
14#include "SkWriteBuffer.h"
15
16sk_sp<SkPathEffect> SkDiscretePathEffect::Make(SkScalar segLength, SkScalar deviation,
17                                               uint32_t seedAssist) {
18    if (!SkScalarIsFinite(segLength) || !SkScalarIsFinite(deviation)) {
19        return nullptr;
20    }
21    if (segLength <= SK_ScalarNearlyZero) {
22        return nullptr;
23    }
24    return sk_sp<SkPathEffect>(new SkDiscretePathEffect(segLength, deviation, seedAssist));
25}
26
27static void Perterb(SkPoint* p, const SkVector& tangent, SkScalar scale) {
28    SkVector normal = tangent;
29    normal.rotateCCW();
30    normal.setLength(scale);
31    *p += normal;
32}
33
34SkDiscretePathEffect::SkDiscretePathEffect(SkScalar segLength,
35                                           SkScalar deviation,
36                                           uint32_t seedAssist)
37    : fSegLength(segLength), fPerterb(deviation), fSeedAssist(seedAssist)
38{
39}
40
41/** \class LCGRandom
42
43    Utility class that implements pseudo random 32bit numbers using a fast
44    linear equation. Unlike rand(), this class holds its own seed (initially
45    set to 0), so that multiple instances can be used with no side-effects.
46
47    Copied from the original implementation of SkRandom. Only contains the
48    methods used by SkDiscretePathEffect::filterPath, with methods that were
49    not called directly moved to private.
50*/
51
52class LCGRandom {
53public:
54    LCGRandom(uint32_t seed) : fSeed(seed) {}
55
56    /** Return the next pseudo random number expressed as a SkScalar
57        in the range [-SK_Scalar1..SK_Scalar1).
58    */
59    SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); }
60
61private:
62    /** Return the next pseudo random number as an unsigned 32bit value.
63    */
64    uint32_t nextU() { uint32_t r = fSeed * kMul + kAdd; fSeed = r; return r; }
65
66    /** Return the next pseudo random number as a signed 32bit value.
67     */
68    int32_t nextS() { return (int32_t)this->nextU(); }
69
70    /** Return the next pseudo random number expressed as a signed SkFixed
71     in the range [-SK_Fixed1..SK_Fixed1).
72     */
73    SkFixed nextSFixed1() { return this->nextS() >> 15; }
74
75    //  See "Numerical Recipes in C", 1992 page 284 for these constants
76    enum {
77        kMul = 1664525,
78        kAdd = 1013904223
79    };
80    uint32_t fSeed;
81};
82
83bool SkDiscretePathEffect::filterPath(SkPath* dst, const SkPath& src,
84                                      SkStrokeRec* rec, const SkRect*) const {
85    bool doFill = rec->isFillStyle();
86
87    SkPathMeasure   meas(src, doFill);
88
89    /* Caller may supply their own seed assist, which by default is 0 */
90    uint32_t seed = fSeedAssist ^ SkScalarRoundToInt(meas.getLength());
91
92    LCGRandom   rand(seed ^ ((seed << 16) | (seed >> 16)));
93    SkScalar    scale = fPerterb;
94    SkPoint     p;
95    SkVector    v;
96
97    do {
98        SkScalar    length = meas.getLength();
99
100        if (fSegLength * (2 + doFill) > length) {
101            meas.getSegment(0, length, dst, true);  // to short for us to mangle
102        } else {
103            int         n = SkScalarRoundToInt(length / fSegLength);
104            SkScalar    delta = length / n;
105            SkScalar    distance = 0;
106
107            if (meas.isClosed()) {
108                n -= 1;
109                distance += delta/2;
110            }
111
112            if (meas.getPosTan(distance, &p, &v)) {
113                Perterb(&p, v, rand.nextSScalar1() * scale);
114                dst->moveTo(p);
115            }
116            while (--n >= 0) {
117                distance += delta;
118                if (meas.getPosTan(distance, &p, &v)) {
119                    Perterb(&p, v, rand.nextSScalar1() * scale);
120                    dst->lineTo(p);
121                }
122            }
123            if (meas.isClosed()) {
124                dst->close();
125            }
126        }
127    } while (meas.nextContour());
128    return true;
129}
130
131sk_sp<SkFlattenable> SkDiscretePathEffect::CreateProc(SkReadBuffer& buffer) {
132    SkScalar segLength = buffer.readScalar();
133    SkScalar perterb = buffer.readScalar();
134    uint32_t seed = buffer.readUInt();
135    return Make(segLength, perterb, seed);
136}
137
138void SkDiscretePathEffect::flatten(SkWriteBuffer& buffer) const {
139    buffer.writeScalar(fSegLength);
140    buffer.writeScalar(fPerterb);
141    buffer.writeUInt(fSeedAssist);
142}
143
144#ifndef SK_IGNORE_TO_STRING
145void SkDiscretePathEffect::toString(SkString* str) const {
146    str->appendf("SkDiscretePathEffect: (");
147    str->appendf("segLength: %.2f deviation: %.2f seed %d", fSegLength, fPerterb, fSeedAssist);
148    str->append(")");
149}
150#endif
151