diff options
Diffstat (limited to 'gfx/skia/skia/src/effects/SkTrimPathEffect.cpp')
-rw-r--r-- | gfx/skia/skia/src/effects/SkTrimPathEffect.cpp | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/effects/SkTrimPathEffect.cpp b/gfx/skia/skia/src/effects/SkTrimPathEffect.cpp new file mode 100644 index 0000000000..abd25224c8 --- /dev/null +++ b/gfx/skia/skia/src/effects/SkTrimPathEffect.cpp @@ -0,0 +1,149 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "include/effects/SkTrimPathEffect.h" + +#include "include/core/SkFlattenable.h" +#include "include/core/SkPath.h" +#include "include/core/SkPathEffect.h" +#include "include/core/SkPathMeasure.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkTPin.h" +#include "src/core/SkReadBuffer.h" +#include "src/core/SkWriteBuffer.h" +#include "src/effects/SkTrimPE.h" + +#include <cstddef> +#include <cstdint> + +class SkMatrix; +class SkStrokeRec; +struct SkRect; + +namespace { + +// Returns the number of contours iterated to satisfy the request. +static size_t add_segments(const SkPath& src, SkScalar start, SkScalar stop, SkPath* dst, + bool requires_moveto = true) { + SkASSERT(start < stop); + + SkPathMeasure measure(src, false); + + SkScalar current_segment_offset = 0; + size_t contour_count = 1; + + do { + const auto next_offset = current_segment_offset + measure.getLength(); + + if (start < next_offset) { + measure.getSegment(start - current_segment_offset, + stop - current_segment_offset, + dst, requires_moveto); + + if (stop <= next_offset) + break; + } + + contour_count++; + current_segment_offset = next_offset; + } while (measure.nextContour()); + + return contour_count; +} + +} // namespace + +SkTrimPE::SkTrimPE(SkScalar startT, SkScalar stopT, SkTrimPathEffect::Mode mode) + : fStartT(startT), fStopT(stopT), fMode(mode) {} + +bool SkTrimPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, + const SkMatrix&) const { + if (fStartT >= fStopT) { + SkASSERT(fMode == SkTrimPathEffect::Mode::kNormal); + return true; + } + + // First pass: compute the total len. + SkScalar len = 0; + SkPathMeasure meas(src, false); + do { + len += meas.getLength(); + } while (meas.nextContour()); + + const auto arcStart = len * fStartT, + arcStop = len * fStopT; + + // Second pass: actually add segments. + if (fMode == SkTrimPathEffect::Mode::kNormal) { + // Normal mode -> one span. + if (arcStart < arcStop) { + add_segments(src, arcStart, arcStop, dst); + } + } else { + // Inverted mode -> one logical span which wraps around at the end -> two actual spans. + // In order to preserve closed path continuity: + // + // 1) add the second/tail span first + // + // 2) skip the head span move-to for single-closed-contour paths + + bool requires_moveto = true; + if (arcStop < len) { + // since we're adding the "tail" first, this is the total number of contours + const auto contour_count = add_segments(src, arcStop, len, dst); + + // if the path consists of a single closed contour, we don't want to disconnect + // the two parts with a moveto. + if (contour_count == 1 && src.isLastContourClosed()) { + requires_moveto = false; + } + } + if (0 < arcStart) { + add_segments(src, 0, arcStart, dst, requires_moveto); + } + } + + return true; +} + +void SkTrimPE::flatten(SkWriteBuffer& buffer) const { + buffer.writeScalar(fStartT); + buffer.writeScalar(fStopT); + buffer.writeUInt(static_cast<uint32_t>(fMode)); +} + +sk_sp<SkFlattenable> SkTrimPE::CreateProc(SkReadBuffer& buffer) { + const auto start = buffer.readScalar(), + stop = buffer.readScalar(); + const auto mode = buffer.readUInt(); + + return SkTrimPathEffect::Make(start, stop, + (mode & 1) ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkPathEffect> SkTrimPathEffect::Make(SkScalar startT, SkScalar stopT, Mode mode) { + if (!SkScalarsAreFinite(startT, stopT)) { + return nullptr; + } + + if (startT <= 0 && stopT >= 1 && mode == Mode::kNormal) { + return nullptr; + } + + startT = SkTPin(startT, 0.f, 1.f); + stopT = SkTPin(stopT, 0.f, 1.f); + + if (startT >= stopT && mode == Mode::kInverted) { + return nullptr; + } + + return sk_sp<SkPathEffect>(new SkTrimPE(startT, stopT, mode)); +} |