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