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 | // Returns the number of contours iterated to satisfy the request. |
17 | static size_t add_segments(const SkPath& src, SkScalar start, SkScalar stop, SkPath* dst, |
18 | bool requires_moveto = true) { |
19 | SkASSERT(start < stop); |
20 | |
21 | SkPathMeasure measure(src, false); |
22 | |
23 | SkScalar current_segment_offset = 0; |
24 | size_t contour_count = 1; |
25 | |
26 | do { |
27 | const auto next_offset = current_segment_offset + measure.getLength(); |
28 | |
29 | if (start < next_offset) { |
30 | measure.getSegment(start - current_segment_offset, |
31 | stop - current_segment_offset, |
32 | dst, requires_moveto); |
33 | |
34 | if (stop <= next_offset) |
35 | break; |
36 | } |
37 | |
38 | contour_count++; |
39 | current_segment_offset = next_offset; |
40 | } while (measure.nextContour()); |
41 | |
42 | return contour_count; |
43 | } |
44 | |
45 | } // namespace |
46 | |
47 | SkTrimPE::SkTrimPE(SkScalar startT, SkScalar stopT, SkTrimPathEffect::Mode mode) |
48 | : fStartT(startT), fStopT(stopT), fMode(mode) {} |
49 | |
50 | bool SkTrimPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const { |
51 | if (fStartT >= fStopT) { |
52 | SkASSERT(fMode == SkTrimPathEffect::Mode::kNormal); |
53 | return true; |
54 | } |
55 | |
56 | // First pass: compute the total len. |
57 | SkScalar len = 0; |
58 | SkPathMeasure meas(src, false); |
59 | do { |
60 | len += meas.getLength(); |
61 | } while (meas.nextContour()); |
62 | |
63 | const auto arcStart = len * fStartT, |
64 | arcStop = len * fStopT; |
65 | |
66 | // Second pass: actually add segments. |
67 | if (fMode == SkTrimPathEffect::Mode::kNormal) { |
68 | // Normal mode -> one span. |
69 | if (arcStart < arcStop) { |
70 | add_segments(src, arcStart, arcStop, dst); |
71 | } |
72 | } else { |
73 | // Inverted mode -> one logical span which wraps around at the end -> two actual spans. |
74 | // In order to preserve closed path continuity: |
75 | // |
76 | // 1) add the second/tail span first |
77 | // |
78 | // 2) skip the head span move-to for single-closed-contour paths |
79 | |
80 | bool requires_moveto = true; |
81 | if (arcStop < len) { |
82 | // since we're adding the "tail" first, this is the total number of contours |
83 | const auto contour_count = add_segments(src, arcStop, len, dst); |
84 | |
85 | // if the path consists of a single closed contour, we don't want to disconnect |
86 | // the two parts with a moveto. |
87 | if (contour_count == 1 && src.isLastContourClosed()) { |
88 | requires_moveto = false; |
89 | } |
90 | } |
91 | if (0 < arcStart) { |
92 | add_segments(src, 0, arcStart, dst, requires_moveto); |
93 | } |
94 | } |
95 | |
96 | return true; |
97 | } |
98 | |
99 | void SkTrimPE::flatten(SkWriteBuffer& buffer) const { |
100 | buffer.writeScalar(fStartT); |
101 | buffer.writeScalar(fStopT); |
102 | buffer.writeUInt(static_cast<uint32_t>(fMode)); |
103 | } |
104 | |
105 | sk_sp<SkFlattenable> SkTrimPE::CreateProc(SkReadBuffer& buffer) { |
106 | const auto start = buffer.readScalar(), |
107 | stop = buffer.readScalar(); |
108 | const auto mode = buffer.readUInt(); |
109 | |
110 | return SkTrimPathEffect::Make(start, stop, |
111 | (mode & 1) ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal); |
112 | } |
113 | |
114 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
115 | |
116 | sk_sp<SkPathEffect> SkTrimPathEffect::Make(SkScalar startT, SkScalar stopT, Mode mode) { |
117 | if (!SkScalarsAreFinite(startT, stopT)) { |
118 | return nullptr; |
119 | } |
120 | |
121 | if (startT <= 0 && stopT >= 1 && mode == Mode::kNormal) { |
122 | return nullptr; |
123 | } |
124 | |
125 | startT = SkTPin(startT, 0.f, 1.f); |
126 | stopT = SkTPin(stopT, 0.f, 1.f); |
127 | |
128 | if (startT >= stopT && mode == Mode::kInverted) { |
129 | return nullptr; |
130 | } |
131 | |
132 | return sk_sp<SkPathEffect>(new SkTrimPE(startT, stopT, mode)); |
133 | } |
134 | |