blob: 7c914915875e9a9c9d6ed98f279891e33bc52c77 [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
Florin Malita0022f5c2020-04-07 08:56:47 -040051bool SkTrimPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const {
Florin Malita827af662018-03-09 16:08:58 -050052 if (fStartT >= fStopT) {
53 SkASSERT(fMode == SkTrimPathEffect::Mode::kNormal);
54 return true;
Mike Reed41232232018-03-07 17:02:47 -050055 }
Florin Malita827af662018-03-09 16:08:58 -050056
57 // First pass: compute the total len.
58 SkScalar len = 0;
59 SkPathMeasure meas(src, false);
60 do {
61 len += meas.getLength();
62 } while (meas.nextContour());
63
64 const auto arcStart = len * fStartT,
65 arcStop = len * fStopT;
66
67 // Second pass: actually add segments.
Florin Malita827af662018-03-09 16:08:58 -050068 if (fMode == SkTrimPathEffect::Mode::kNormal) {
Florin Malita0022f5c2020-04-07 08:56:47 -040069 // Normal mode -> one span.
70 if (arcStart < arcStop) {
71 add_segments(src, arcStart, arcStop, dst);
72 }
Florin Malita827af662018-03-09 16:08:58 -050073 } else {
Florin Malita0022f5c2020-04-07 08:56:47 -040074 // Inverted mode -> one logical span which wraps around at the end -> two actual spans.
75 // In order to preserve closed path continuity:
76 //
77 // 1) add the second/tail span first
78 //
79 // 2) skip the head span move-to for single-closed-contour paths
80
81 bool requires_moveto = true;
82 if (arcStop < len) {
83 // since we're adding the "tail" first, this is the total number of contours
84 const auto contour_count = add_segments(src, arcStop, len, dst);
85
86 // if the path consists of a single closed contour, we don't want to disconnect
87 // the two parts with a moveto.
88 if (contour_count == 1 && src.isLastContourClosed()) {
89 requires_moveto = false;
90 }
91 }
92 if (0 < arcStart) {
93 add_segments(src, 0, arcStart, dst, requires_moveto);
94 }
Florin Malita827af662018-03-09 16:08:58 -050095 }
96
Mike Reed41232232018-03-07 17:02:47 -050097 return true;
98}
99
100void SkTrimPE::flatten(SkWriteBuffer& buffer) const {
101 buffer.writeScalar(fStartT);
102 buffer.writeScalar(fStopT);
Florin Malita827af662018-03-09 16:08:58 -0500103 buffer.writeUInt(static_cast<uint32_t>(fMode));
Mike Reed41232232018-03-07 17:02:47 -0500104}
105
106sk_sp<SkFlattenable> SkTrimPE::CreateProc(SkReadBuffer& buffer) {
Florin Malita827af662018-03-09 16:08:58 -0500107 const auto start = buffer.readScalar(),
108 stop = buffer.readScalar();
109 const auto mode = buffer.readUInt();
110
111 return SkTrimPathEffect::Make(start, stop,
112 (mode & 1) ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal);
Mike Reed41232232018-03-07 17:02:47 -0500113}
114
Mike Reed41232232018-03-07 17:02:47 -0500115//////////////////////////////////////////////////////////////////////////////////////////////////
116
Florin Malita827af662018-03-09 16:08:58 -0500117sk_sp<SkPathEffect> SkTrimPathEffect::Make(SkScalar startT, SkScalar stopT, Mode mode) {
Mike Reed41232232018-03-07 17:02:47 -0500118 if (!SkScalarsAreFinite(startT, stopT)) {
119 return nullptr;
120 }
Florin Malita827af662018-03-09 16:08:58 -0500121
122 if (startT <= 0 && stopT >= 1 && mode == Mode::kNormal) {
Mike Reed41232232018-03-07 17:02:47 -0500123 return nullptr;
124 }
Florin Malita827af662018-03-09 16:08:58 -0500125
126 startT = SkTPin(startT, 0.f, 1.f);
127 stopT = SkTPin(stopT, 0.f, 1.f);
128
129 if (startT >= stopT && mode == Mode::kInverted) {
130 return nullptr;
131 }
132
133 return sk_sp<SkPathEffect>(new SkTrimPE(startT, stopT, mode));
Mike Reed41232232018-03-07 17:02:47 -0500134}