• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Make(SkScalar segLength,SkScalar deviation,uint32_t seedAssist)16 sk_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 
Perterb(SkPoint * p,const SkVector & tangent,SkScalar scale)27 static 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 
SkDiscretePathEffect(SkScalar segLength,SkScalar deviation,uint32_t seedAssist)34 SkDiscretePathEffect::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 
52 class LCGRandom {
53 public:
LCGRandom(uint32_t seed)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     */
nextSScalar1()59     SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); }
60 
61 private:
62     /** Return the next pseudo random number as an unsigned 32bit value.
63     */
nextU()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      */
nextS()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      */
nextSFixed1()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 
filterPath(SkPath * dst,const SkPath & src,SkStrokeRec * rec,const SkRect *) const83 bool 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 
CreateProc(SkReadBuffer & buffer)131 sk_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 
flatten(SkWriteBuffer & buffer) const138 void 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
toString(SkString * str) const145 void 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