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 "include/core/SkPathMeasure.h"
9 #include "include/effects/SkTrimPathEffect.h"
10 #include "src/core/SkReadBuffer.h"
11 #include "src/core/SkWriteBuffer.h"
12 #include "src/effects/SkTrimPE.h"
13
14 namespace {
15
16 class Segmentator : public SkNoncopyable {
17 public:
Segmentator(const SkPath & src,SkPath * dst)18 Segmentator(const SkPath& src, SkPath* dst)
19 : fMeasure(src, false)
20 , fDst(dst) {}
21
add(SkScalar start,SkScalar stop)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
SkTrimPE(SkScalar startT,SkScalar stopT,SkTrimPathEffect::Mode mode)53 SkTrimPE::SkTrimPE(SkScalar startT, SkScalar stopT, SkTrimPathEffect::Mode mode)
54 : fStartT(startT), fStopT(stopT), fMode(mode) {}
55
onFilterPath(SkPath * dst,const SkPath & src,SkStrokeRec * rec,const SkRect * cullRect) const56 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
flatten(SkWriteBuffer & buffer) const85 void SkTrimPE::flatten(SkWriteBuffer& buffer) const {
86 buffer.writeScalar(fStartT);
87 buffer.writeScalar(fStopT);
88 buffer.writeUInt(static_cast<uint32_t>(fMode));
89 }
90
CreateProc(SkReadBuffer & buffer)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
Make(SkScalar startT,SkScalar stopT,Mode mode)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