/* * 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(fMode)); } sk_sp 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 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(new SkTrimPE(startT, stopT, mode)); }