blob: 51307f6e22755d3d6d5a4154bd2490d760accf53 [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"
Mike Klein8aa0edf2020-10-16 11:04:18 -050010#include "include/private/SkTPin.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "src/core/SkReadBuffer.h"
12#include "src/core/SkWriteBuffer.h"
13#include "src/effects/SkTrimPE.h"
Mike Reed41232232018-03-07 17:02:47 -050014
Florin Malita827af662018-03-09 16:08:58 -050015namespace {
16
Florin Malita0022f5c2020-04-07 08:56:47 -040017// Returns the number of contours iterated to satisfy the request.
18static size_t add_segments(const SkPath& src, SkScalar start, SkScalar stop, SkPath* dst,
19 bool requires_moveto = true) {
20 SkASSERT(start < stop);
Florin Malita827af662018-03-09 16:08:58 -050021
Florin Malita0022f5c2020-04-07 08:56:47 -040022 SkPathMeasure measure(src, false);
Florin Malita827af662018-03-09 16:08:58 -050023
Florin Malita0022f5c2020-04-07 08:56:47 -040024 SkScalar current_segment_offset = 0;
25 size_t contour_count = 1;
Florin Malita827af662018-03-09 16:08:58 -050026
Florin Malita0022f5c2020-04-07 08:56:47 -040027 do {
28 const auto next_offset = current_segment_offset + measure.getLength();
Florin Malita827af662018-03-09 16:08:58 -050029
Florin Malita0022f5c2020-04-07 08:56:47 -040030 if (start < next_offset) {
31 measure.getSegment(start - current_segment_offset,
32 stop - current_segment_offset,
33 dst, requires_moveto);
Florin Malita827af662018-03-09 16:08:58 -050034
Florin Malita0022f5c2020-04-07 08:56:47 -040035 if (stop <= next_offset)
36 break;
37 }
Florin Malita827af662018-03-09 16:08:58 -050038
Florin Malita0022f5c2020-04-07 08:56:47 -040039 contour_count++;
40 current_segment_offset = next_offset;
41 } while (measure.nextContour());
Florin Malita827af662018-03-09 16:08:58 -050042
Florin Malita0022f5c2020-04-07 08:56:47 -040043 return contour_count;
44}
Florin Malita827af662018-03-09 16:08:58 -050045
46} // namespace
47
48SkTrimPE::SkTrimPE(SkScalar startT, SkScalar stopT, SkTrimPathEffect::Mode mode)
49 : fStartT(startT), fStopT(stopT), fMode(mode) {}
Mike Reed41232232018-03-07 17:02:47 -050050
Tyler Dennistonf8b7c1a2021-07-13 13:22:19 -040051bool SkTrimPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*,
52 const SkMatrix&) const {
Florin Malita827af662018-03-09 16:08:58 -050053 if (fStartT >= fStopT) {
54 SkASSERT(fMode == SkTrimPathEffect::Mode::kNormal);
55 return true;
Mike Reed41232232018-03-07 17:02:47 -050056 }
Florin Malita827af662018-03-09 16:08:58 -050057
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.
Florin Malita827af662018-03-09 16:08:58 -050069 if (fMode == SkTrimPathEffect::Mode::kNormal) {
Florin Malita0022f5c2020-04-07 08:56:47 -040070 // Normal mode -> one span.
71 if (arcStart < arcStop) {
72 add_segments(src, arcStart, arcStop, dst);
73 }
Florin Malita827af662018-03-09 16:08:58 -050074 } else {
Florin Malita0022f5c2020-04-07 08:56:47 -040075 // 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 }
Florin Malita827af662018-03-09 16:08:58 -050096 }
97
Mike Reed41232232018-03-07 17:02:47 -050098 return true;
99}
100
101void SkTrimPE::flatten(SkWriteBuffer& buffer) const {
102 buffer.writeScalar(fStartT);
103 buffer.writeScalar(fStopT);
Florin Malita827af662018-03-09 16:08:58 -0500104 buffer.writeUInt(static_cast<uint32_t>(fMode));
Mike Reed41232232018-03-07 17:02:47 -0500105}
106
107sk_sp<SkFlattenable> SkTrimPE::CreateProc(SkReadBuffer& buffer) {
Florin Malita827af662018-03-09 16:08:58 -0500108 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);
Mike Reed41232232018-03-07 17:02:47 -0500114}
115
Mike Reed41232232018-03-07 17:02:47 -0500116//////////////////////////////////////////////////////////////////////////////////////////////////
117
Florin Malita827af662018-03-09 16:08:58 -0500118sk_sp<SkPathEffect> SkTrimPathEffect::Make(SkScalar startT, SkScalar stopT, Mode mode) {
Mike Reed41232232018-03-07 17:02:47 -0500119 if (!SkScalarsAreFinite(startT, stopT)) {
120 return nullptr;
121 }
Florin Malita827af662018-03-09 16:08:58 -0500122
123 if (startT <= 0 && stopT >= 1 && mode == Mode::kNormal) {
Mike Reed41232232018-03-07 17:02:47 -0500124 return nullptr;
125 }
Florin Malita827af662018-03-09 16:08:58 -0500126
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));
Mike Reed41232232018-03-07 17:02:47 -0500135}