summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/effects/SkDiscretePathEffect.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/skia/skia/src/effects/SkDiscretePathEffect.cpp191
1 files changed, 191 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/effects/SkDiscretePathEffect.cpp b/gfx/skia/skia/src/effects/SkDiscretePathEffect.cpp
new file mode 100644
index 0000000000..1453d5d7c8
--- /dev/null
+++ b/gfx/skia/skia/src/effects/SkDiscretePathEffect.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkPathEffect.h"
+#include "include/core/SkPathMeasure.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkStrokeRec.h"
+#include "include/core/SkTypes.h"
+#include "include/effects/SkDiscretePathEffect.h"
+#include "include/private/base/SkFixed.h"
+#include "src/core/SkPathEffectBase.h"
+#include "src/core/SkPointPriv.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
+
+#include <algorithm>
+#include <cstdint>
+
+class SkMatrix;
+
+/** \class LCGRandom
+
+ Utility class that implements pseudo random 32bit numbers using a fast
+ linear equation. Unlike rand(), this class holds its own seed (initially
+ set to 0), so that multiple instances can be used with no side-effects.
+
+ Copied from the original implementation of SkRandom. Only contains the
+ methods used by SkDiscretePathEffect::filterPath, with methods that were
+ not called directly moved to private.
+*/
+class LCGRandom {
+public:
+ LCGRandom(uint32_t seed) : fSeed(seed) {}
+
+ /** Return the next pseudo random number expressed as a SkScalar
+ in the range [-SK_Scalar1..SK_Scalar1).
+ */
+ SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); }
+
+private:
+ /** Return the next pseudo random number as an unsigned 32bit value.
+ */
+ uint32_t nextU() { uint32_t r = fSeed * kMul + kAdd; fSeed = r; return r; }
+
+ /** Return the next pseudo random number as a signed 32bit value.
+ */
+ int32_t nextS() { return (int32_t)this->nextU(); }
+
+ /** Return the next pseudo random number expressed as a signed SkFixed
+ in the range [-SK_Fixed1..SK_Fixed1).
+ */
+ SkFixed nextSFixed1() { return this->nextS() >> 15; }
+
+ // See "Numerical Recipes in C", 1992 page 284 for these constants
+ enum {
+ kMul = 1664525,
+ kAdd = 1013904223
+ };
+ uint32_t fSeed;
+};
+
+static void Perterb(SkPoint* p, const SkVector& tangent, SkScalar scale) {
+ SkVector normal = tangent;
+ SkPointPriv::RotateCCW(&normal);
+ normal.setLength(scale);
+ *p += normal;
+}
+
+class SK_API SkDiscretePathEffectImpl : public SkPathEffectBase {
+public:
+ SkDiscretePathEffectImpl(SkScalar segLength, SkScalar deviation, uint32_t seedAssist)
+ : fSegLength(segLength), fPerterb(deviation), fSeedAssist(seedAssist)
+ {
+ SkASSERT(SkScalarIsFinite(segLength));
+ SkASSERT(SkScalarIsFinite(deviation));
+ SkASSERT(segLength > SK_ScalarNearlyZero);
+ }
+
+ bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
+ const SkRect*, const SkMatrix&) const override {
+ bool doFill = rec->isFillStyle();
+
+ SkPathMeasure meas(src, doFill);
+
+ /* Caller may supply their own seed assist, which by default is 0 */
+ uint32_t seed = fSeedAssist ^ SkScalarRoundToInt(meas.getLength());
+
+ LCGRandom rand(seed ^ ((seed << 16) | (seed >> 16)));
+ SkScalar scale = fPerterb;
+ SkPoint p;
+ SkVector v;
+
+ do {
+ SkScalar length = meas.getLength();
+ #if defined(SK_BUILD_FOR_FUZZER)
+ if (length > 1000) {
+ return false;
+ }
+ #endif
+
+ if (fSegLength * (2 + doFill) > length) {
+ meas.getSegment(0, length, dst, true); // to short for us to mangle
+ } else {
+ int n = SkScalarRoundToInt(length / fSegLength);
+ constexpr int kMaxReasonableIterations = 100000;
+ n = std::min(n, kMaxReasonableIterations);
+ SkScalar delta = length / n;
+ SkScalar distance = 0;
+
+ if (meas.isClosed()) {
+ n -= 1;
+ distance += delta/2;
+ }
+
+ if (meas.getPosTan(distance, &p, &v)) {
+ Perterb(&p, v, rand.nextSScalar1() * scale);
+ dst->moveTo(p);
+ }
+ while (--n >= 0) {
+ distance += delta;
+ if (meas.getPosTan(distance, &p, &v)) {
+ Perterb(&p, v, rand.nextSScalar1() * scale);
+ dst->lineTo(p);
+ }
+ }
+ if (meas.isClosed()) {
+ dst->close();
+ }
+ }
+ } while (meas.nextContour());
+ return true;
+ }
+
+ bool computeFastBounds(SkRect* bounds) const override {
+ if (bounds) {
+ SkScalar maxOutset = SkScalarAbs(fPerterb);
+ bounds->outset(maxOutset, maxOutset);
+ }
+ return true;
+ }
+
+ static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
+ SkScalar segLength = buffer.readScalar();
+ SkScalar perterb = buffer.readScalar();
+ uint32_t seed = buffer.readUInt();
+ return SkDiscretePathEffect::Make(segLength, perterb, seed);
+ }
+
+ void flatten(SkWriteBuffer& buffer) const override {
+ buffer.writeScalar(fSegLength);
+ buffer.writeScalar(fPerterb);
+ buffer.writeUInt(fSeedAssist);
+ }
+
+ Factory getFactory() const override { return CreateProc; }
+ const char* getTypeName() const override { return "SkDiscretePathEffect"; }
+
+private:
+ const SkScalar fSegLength,
+ fPerterb;
+ /* Caller-supplied 32 bit seed assist */
+ const uint32_t fSeedAssist;
+
+ using INHERITED = SkPathEffectBase;
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkPathEffect> SkDiscretePathEffect::Make(SkScalar segLength, SkScalar deviation,
+ uint32_t seedAssist) {
+ if (!SkScalarIsFinite(segLength) || !SkScalarIsFinite(deviation)) {
+ return nullptr;
+ }
+ if (segLength <= SK_ScalarNearlyZero) {
+ return nullptr;
+ }
+ return sk_sp<SkPathEffect>(new SkDiscretePathEffectImpl(segLength, deviation, seedAssist));
+}
+
+void SkDiscretePathEffect::RegisterFlattenables() {
+ SkFlattenable::Register("SkDiscretePathEffect", SkDiscretePathEffectImpl::CreateProc);
+}