blob: bb7a99e74acfc0902cd6598f4335f61bd418b1d5 [file] [log] [blame]
Mike Reed41232232018-03-07 17:02:47 -05001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#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"
Mike Reed41232232018-03-07 17:02:47 -050013
Florin Malita827af662018-03-09 16:08:58 -050014namespace {
15
Florin Malita0022f5c2020-04-07 08:56:47 -040016// Returns the number of contours iterated to satisfy the request.
17static size_t add_segments(const SkPath& src, SkScalar start, SkScalar stop, SkPath* dst,
18 bool requires_moveto = true) {
19 SkASSERT(start < stop);
Florin Malita827af662018-03-09 16:08:58 -050020
Florin Malita0022f5c2020-04-07 08:56:47 -040021 SkPathMeasure measure(src, false);
Florin Malita827af662018-03-09 16:08:58 -050022
Florin Malita0022f5c2020-04-07 08:56:47 -040023 SkScalar current_segment_offset = 0;
24 size_t contour_count = 1;
Florin Malita827af662018-03-09 16:08:58 -050025
Florin Malita0022f5c2020-04-07 08:56:47 -040026 do {
27 const auto next_offset = current_segment_offset + measure.getLength();
Florin Malita827af662018-03-09 16:08:58 -050028
Florin Malita0022f5c2020-04-07 08:56:47 -040029 if (start < next_offset) {
30 measure.getSegment(start - current_segment_offset,
31 stop - current_segment_offset,
32 dst, requires_moveto);
Florin Malita827af662018-03-09 16:08:58 -050033
Florin Malita0022f5c2020-04-07 08:56:47 -040034 if (stop <= next_offset)
35 break;
36 }
Florin Malita827af662018-03-09 16:08:58 -050037
Florin Malita0022f5c2020-04-07 08:56:47 -040038 contour_count++;
39 current_segment_offset = next_offset;
40 } while (measure.nextContour());
Florin Malita827af662018-03-09 16:08:58 -050041
Florin Malita0022f5c2020-04-07 08:56:47 -040042 return contour_count;
43}
Florin Malita827af662018-03-09 16:08:58 -050044
45} // namespace
46
47SkTrimPE::SkTrimPE(SkScalar startT, SkScalar stopT, SkTrimPathEffect::Mode mode)
48 : fStartT(startT), fStopT(stopT), fMode(mode) {}
Mike Reed41232232018-03-07 17:02:47 -050049
Florin Malita0022f5c2020-04-07 08:56:47 -040050bool SkTrimPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const {
Florin Malita827af662018-03-09 16:08:58 -050051 if (fStartT >= fStopT) {
52 SkASSERT(fMode == SkTrimPathEffect::Mode::kNormal);
53 return true;
Mike Reed41232232018-03-07 17:02:47 -050054 }
Florin Malita827af662018-03-09 16:08:58 -050055
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.
Florin Malita827af662018-03-09 16:08:58 -050067 if (fMode == SkTrimPathEffect::Mode::kNormal) {
Florin Malita0022f5c2020-04-07 08:56:47 -040068 // Normal mode -> one span.
69 if (arcStart < arcStop) {
70 add_segments(src, arcStart, arcStop, dst);
71 }
Florin Malita827af662018-03-09 16:08:58 -050072 } else {
Florin Malita0022f5c2020-04-07 08:56:47 -040073 // 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 }
Florin Malita827af662018-03-09 16:08:58 -050094 }
95
Mike Reed41232232018-03-07 17:02:47 -050096 return true;
97}
98
99void SkTrimPE::flatten(SkWriteBuffer& buffer) const {
100 buffer.writeScalar(fStartT);
101 buffer.writeScalar(fStopT);
Florin Malita827af662018-03-09 16:08:58 -0500102 buffer.writeUInt(static_cast<uint32_t>(fMode));
Mike Reed41232232018-03-07 17:02:47 -0500103}
104
105sk_sp<SkFlattenable> SkTrimPE::CreateProc(SkReadBuffer& buffer) {
Florin Malita827af662018-03-09 16:08:58 -0500106 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);
Mike Reed41232232018-03-07 17:02:47 -0500112}
113
Mike Reed41232232018-03-07 17:02:47 -0500114//////////////////////////////////////////////////////////////////////////////////////////////////
115
Florin Malita827af662018-03-09 16:08:58 -0500116sk_sp<SkPathEffect> SkTrimPathEffect::Make(SkScalar startT, SkScalar stopT, Mode mode) {
Mike Reed41232232018-03-07 17:02:47 -0500117 if (!SkScalarsAreFinite(startT, stopT)) {
118 return nullptr;
119 }
Florin Malita827af662018-03-09 16:08:58 -0500120
121 if (startT <= 0 && stopT >= 1 && mode == Mode::kNormal) {
Mike Reed41232232018-03-07 17:02:47 -0500122 return nullptr;
123 }
Florin Malita827af662018-03-09 16:08:58 -0500124
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));
Mike Reed41232232018-03-07 17:02:47 -0500133}