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