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