diff options
Diffstat (limited to 'gfx/skia/skia/src/effects/SkTrimPathEffect.cpp')
-rw-r--r-- | gfx/skia/skia/src/effects/SkTrimPathEffect.cpp | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/effects/SkTrimPathEffect.cpp b/gfx/skia/skia/src/effects/SkTrimPathEffect.cpp new file mode 100644 index 0000000000..d0a8806d40 --- /dev/null +++ b/gfx/skia/skia/src/effects/SkTrimPathEffect.cpp @@ -0,0 +1,119 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "include/core/SkPathMeasure.h" +#include "include/effects/SkTrimPathEffect.h" +#include "src/core/SkReadBuffer.h" +#include "src/core/SkWriteBuffer.h" +#include "src/effects/SkTrimPE.h" + +namespace { + +class Segmentator : public SkNoncopyable { +public: + Segmentator(const SkPath& src, SkPath* dst) + : fMeasure(src, false) + , fDst(dst) {} + + void add(SkScalar start, SkScalar stop) { + SkASSERT(start < stop); + + // TODO: we appear to skip zero-length contours. + do { + const auto nextOffset = fCurrentSegmentOffset + fMeasure.getLength(); + + if (start < nextOffset) { + fMeasure.getSegment(start - fCurrentSegmentOffset, + stop - fCurrentSegmentOffset, + fDst, true); + + if (stop < nextOffset) + break; + } + + fCurrentSegmentOffset = nextOffset; + } while (fMeasure.nextContour()); + } + +private: + SkPathMeasure fMeasure; + SkPath* fDst; + + SkScalar fCurrentSegmentOffset = 0; + + using INHERITED = SkNoncopyable; +}; + +} // namespace + +SkTrimPE::SkTrimPE(SkScalar startT, SkScalar stopT, SkTrimPathEffect::Mode mode) + : fStartT(startT), fStopT(stopT), fMode(mode) {} + +bool SkTrimPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, + const SkRect* cullRect) const { + if (fStartT >= fStopT) { + SkASSERT(fMode == SkTrimPathEffect::Mode::kNormal); + return true; + } + + // First pass: compute the total len. + SkScalar len = 0; + SkPathMeasure meas(src, false); + do { + len += meas.getLength(); + } while (meas.nextContour()); + + const auto arcStart = len * fStartT, + arcStop = len * fStopT; + + // Second pass: actually add segments. + Segmentator segmentator(src, dst); + if (fMode == SkTrimPathEffect::Mode::kNormal) { + if (arcStart < arcStop) segmentator.add(arcStart, arcStop); + } else { + if (0 < arcStart) segmentator.add(0, arcStart); + if (arcStop < len) segmentator.add(arcStop, len); + } + + return true; +} + +void SkTrimPE::flatten(SkWriteBuffer& buffer) const { + buffer.writeScalar(fStartT); + buffer.writeScalar(fStopT); + buffer.writeUInt(static_cast<uint32_t>(fMode)); +} + +sk_sp<SkFlattenable> SkTrimPE::CreateProc(SkReadBuffer& buffer) { + const auto start = buffer.readScalar(), + stop = buffer.readScalar(); + const auto mode = buffer.readUInt(); + + return SkTrimPathEffect::Make(start, stop, + (mode & 1) ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkPathEffect> SkTrimPathEffect::Make(SkScalar startT, SkScalar stopT, Mode mode) { + if (!SkScalarsAreFinite(startT, stopT)) { + return nullptr; + } + + if (startT <= 0 && stopT >= 1 && mode == Mode::kNormal) { + return nullptr; + } + + startT = SkTPin(startT, 0.f, 1.f); + stopT = SkTPin(stopT, 0.f, 1.f); + + if (startT >= stopT && mode == Mode::kInverted) { + return nullptr; + } + + return sk_sp<SkPathEffect>(new SkTrimPE(startT, stopT, mode)); +} |