1 /* 2 * Copyright 2018 Google Inc. 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 #include "SkPathMeasure.h" 9 #include "SkTrimPathEffect.h" 10 #include "SkTrimPE.h" 11 #include "SkReadBuffer.h" 12 #include "SkWriteBuffer.h" 13 14 namespace { 15 16 class Segmentator : public SkNoncopyable { 17 public: 18 Segmentator(const SkPath& src, SkPath* dst) 19 : fMeasure(src, false) 20 , fDst(dst) {} 21 22 void add(SkScalar start, SkScalar stop) { 23 SkASSERT(start < stop); 24 25 // TODO: we appear to skip zero-length contours. 26 do { 27 const auto nextOffset = fCurrentSegmentOffset + fMeasure.getLength(); 28 29 if (start < nextOffset) { 30 fMeasure.getSegment(start - fCurrentSegmentOffset, 31 stop - fCurrentSegmentOffset, 32 fDst, true); 33 34 if (stop < nextOffset) 35 break; 36 } 37 38 fCurrentSegmentOffset = nextOffset; 39 } while (fMeasure.nextContour()); 40 } 41 42 private: 43 SkPathMeasure fMeasure; 44 SkPath* fDst; 45 46 SkScalar fCurrentSegmentOffset = 0; 47 48 using INHERITED = SkNoncopyable; 49 }; 50 51 } // namespace 52 53 SkTrimPE::SkTrimPE(SkScalar startT, SkScalar stopT, SkTrimPathEffect::Mode mode) 54 : fStartT(startT), fStopT(stopT), fMode(mode) {} 55 56 bool SkTrimPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, 57 const SkRect* cullRect) const { 58 if (fStartT >= fStopT) { 59 SkASSERT(fMode == SkTrimPathEffect::Mode::kNormal); 60 return true; 61 } 62 63 // First pass: compute the total len. 64 SkScalar len = 0; 65 SkPathMeasure meas(src, false); 66 do { 67 len += meas.getLength(); 68 } while (meas.nextContour()); 69 70 const auto arcStart = len * fStartT, 71 arcStop = len * fStopT; 72 73 // Second pass: actually add segments. 74 Segmentator segmentator(src, dst); 75 if (fMode == SkTrimPathEffect::Mode::kNormal) { 76 if (arcStart < arcStop) segmentator.add(arcStart, arcStop); 77 } else { 78 if (0 < arcStart) segmentator.add(0, arcStart); 79 if (arcStop < len) segmentator.add(arcStop, len); 80 } 81 82 return true; 83 } 84 85 void SkTrimPE::flatten(SkWriteBuffer& buffer) const { 86 buffer.writeScalar(fStartT); 87 buffer.writeScalar(fStopT); 88 buffer.writeUInt(static_cast<uint32_t>(fMode)); 89 } 90 91 sk_sp<SkFlattenable> SkTrimPE::CreateProc(SkReadBuffer& buffer) { 92 const auto start = buffer.readScalar(), 93 stop = buffer.readScalar(); 94 const auto mode = buffer.readUInt(); 95 96 return SkTrimPathEffect::Make(start, stop, 97 (mode & 1) ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal); 98 } 99 100 ////////////////////////////////////////////////////////////////////////////////////////////////// 101 102 sk_sp<SkPathEffect> SkTrimPathEffect::Make(SkScalar startT, SkScalar stopT, Mode mode) { 103 if (!SkScalarsAreFinite(startT, stopT)) { 104 return nullptr; 105 } 106 107 if (startT <= 0 && stopT >= 1 && mode == Mode::kNormal) { 108 return nullptr; 109 } 110 111 startT = SkTPin(startT, 0.f, 1.f); 112 stopT = SkTPin(stopT, 0.f, 1.f); 113 114 if (startT >= stopT && mode == Mode::kInverted) { 115 return nullptr; 116 } 117 118 return sk_sp<SkPathEffect>(new SkTrimPE(startT, stopT, mode)); 119 } 120