aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/dashcubics.cpp110
-rw-r--r--include/effects/SkTrimPathEffect.h11
-rw-r--r--src/effects/SkTrimPE.h9
-rw-r--r--src/effects/SkTrimPathEffect.cpp94
4 files changed, 180 insertions, 44 deletions
diff --git a/gm/dashcubics.cpp b/gm/dashcubics.cpp
index 1d80d75b4a..bfff3f3658 100644
--- a/gm/dashcubics.cpp
+++ b/gm/dashcubics.cpp
@@ -6,10 +6,13 @@
*/
#include "gm.h"
+#include "SkAnimTimer.h"
#include "SkCanvas.h"
+#include "SkDashPathEffect.h"
#include "SkPath.h"
#include "SkParsePath.h"
-#include "SkDashPathEffect.h"
+#include "SkTArray.h"
+#include "SkTrimPathEffect.h"
/*
* Inspired by http://code.google.com/p/chromium/issues/detail?id=112145
@@ -59,41 +62,104 @@ DEF_SIMPLE_GM(dashcubics, canvas, 865, 750) {
}
}
-#include "SkTrimPathEffect.h"
class TrimGM : public skiagm::GM {
public:
- TrimGM() {}
+ TrimGM() {
+ SkAssertResult(SkParsePath::FromSVGString(
+ "M 0,100 C 10, 50 190, 50 200,100"
+ "M 200,100 C 210,150 390,150 400,100"
+ "M 400,100 C 390, 50 210, 50 200,100"
+ "M 200,100 C 190,150 10,150 0,100",
+ &fPaths.push_back()));
+
+ SkAssertResult(SkParsePath::FromSVGString(
+ "M 0, 75 L 200, 75"
+ "M 200, 91 L 200, 91"
+ "M 200,108 L 200,108"
+ "M 200,125 L 400,125",
+ &fPaths.push_back()));
+
+ SkAssertResult(SkParsePath::FromSVGString(
+ "M 0,100 L 50, 50"
+ "M 50, 50 L 150,150"
+ "M 150,150 L 250, 50"
+ "M 250, 50 L 350,150"
+ "M 350,150 L 400,100",
+ &fPaths.push_back()));
+
+ }
protected:
SkString onShortName() override { return SkString("trimpatheffect"); }
- SkISize onISize() override { return SkISize::Make(1240, 390); }
+ SkISize onISize() override {
+ return SkISize::Make(1400, 1000);
+ }
void onDraw(SkCanvas* canvas) override {
- SkPaint paint;
- paint.setPathEffect(SkTrimPathEffect::Make(0.25 + fOffset, 0.75));
- paint.setStyle(SkPaint::kStroke_Style);
- paint.setAntiAlias(true);
- paint.setStrokeWidth(10);
+ static constexpr SkSize kCellSize = { 440, 150 };
+ static constexpr SkScalar kOffsets[][2] = {
+ { -0.33f, -0.66f },
+ { 0 , 1 },
+ { 0 , 0.25f},
+ { 0.25f, 0.75f},
+ { 0.75f, 1 },
+ { 1 , 0.75f},
+ };
- SkPath path;
- path.moveTo(50, 300);
- path.cubicTo(100, 50, 150, 550, 200, 300);
-
- paint.setColor(0xFF888888);
- canvas->drawPath(path, paint);
- paint.setPathEffect(nullptr);
- paint.setStrokeWidth(0);
- paint.setColor(0xFF000000);
- canvas->drawPath(path, paint);
+ SkPaint hairlinePaint;
+ hairlinePaint.setAntiAlias(true);
+ hairlinePaint.setStyle(SkPaint::kStroke_Style);
+ hairlinePaint.setStrokeCap(SkPaint::kRound_Cap);
+ hairlinePaint.setStrokeWidth(2);
+ SkPaint normalPaint = hairlinePaint;
+ normalPaint.setStrokeWidth(10);
+ normalPaint.setColor(0x8000ff00);
+ SkPaint invertedPaint = normalPaint;
+ invertedPaint.setColor(0x80ff0000);
+
+ for (const auto& offset : kOffsets) {
+ auto start = offset[0] + fOffset,
+ stop = offset[1] + fOffset;
+
+ auto normalMode = SkTrimPathEffect::Mode::kNormal,
+ invertedMode = SkTrimPathEffect::Mode::kInverted;
+ if (fOffset) {
+ start -= SkScalarFloorToScalar(start);
+ stop -= SkScalarFloorToScalar(stop);
+ if (start > stop) {
+ SkTSwap(start, stop);
+ SkTSwap(normalMode, invertedMode);
+ }
+ }
+
+ normalPaint.setPathEffect(SkTrimPathEffect::Make(start, stop, normalMode));
+ invertedPaint.setPathEffect(SkTrimPathEffect::Make(start, stop, invertedMode));
+
+ {
+ SkAutoCanvasRestore acr(canvas, true);
+ for (const auto& path : fPaths) {
+ canvas->drawPath(path, normalPaint);
+ canvas->drawPath(path, invertedPaint);
+ canvas->drawPath(path, hairlinePaint);
+ canvas->translate(kCellSize.width(), 0);
+ }
+ }
+
+ canvas->translate(0, kCellSize.height());
+ }
}
- bool onAnimate(const SkAnimTimer&) override {
- // fOffset += 1;
+ bool onAnimate(const SkAnimTimer& t) override {
+ fOffset = t.msec() / 2000.0f;
+ fOffset -= floorf(fOffset);
return true;
}
+
private:
- SkScalar fOffset = 0;
+ SkTArray<SkPath> fPaths;
+ SkScalar fOffset = 0;
+
typedef skiagm::GM INHERITED;
};
DEF_GM(return new TrimGM;)
diff --git a/include/effects/SkTrimPathEffect.h b/include/effects/SkTrimPathEffect.h
index d40c9e2bbf..e96b94f83e 100644
--- a/include/effects/SkTrimPathEffect.h
+++ b/include/effects/SkTrimPathEffect.h
@@ -12,6 +12,11 @@
class SK_API SkTrimPathEffect {
public:
+ enum class Mode {
+ kNormal, // return the subset path [start,stop]
+ kInverted, // return the complement/subset paths [0,start] + [stop,1]
+ };
+
/**
* Take start and stop "t" values (values between 0...1), and return a path that is that
* subset of the original path.
@@ -26,11 +31,11 @@ public:
* startT and stopT must be 0..1 inclusive. If they are outside of that interval, they will
* be pinned to the nearest legal value. If either is NaN, null will be returned.
*
- * Note: if startT < stopT, this will return one (logical) segment (even if it is spread
- * across multiple contours). If startT > stopT, then this will return 2 logical
+ * Note: for Mode::kNormal, this will return one (logical) segment (even if it is spread
+ * across multiple contours). For Mode::kInverted, this will return 2 logical
* segments: 0...stopT and startT...1
*/
- static sk_sp<SkPathEffect> Make(SkScalar startT, SkScalar stopT);
+ static sk_sp<SkPathEffect> Make(SkScalar startT, SkScalar stopT, Mode = Mode::kNormal);
};
#endif
diff --git a/src/effects/SkTrimPE.h b/src/effects/SkTrimPE.h
index 2cd39c4793..fde3292a67 100644
--- a/src/effects/SkTrimPE.h
+++ b/src/effects/SkTrimPE.h
@@ -10,9 +10,11 @@
#include "SkPathEffect.h"
+#include "SkTrimPathEffect.h"
+
class SkTrimPE : public SkPathEffect {
public:
- SkTrimPE(SkScalar startT, SkScalar stopT);
+ SkTrimPE(SkScalar startT, SkScalar stopT, SkTrimPathEffect::Mode);
bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override;
@@ -23,8 +25,9 @@ protected:
void flatten(SkWriteBuffer&) const override;
private:
- const SkScalar fStartT;
- const SkScalar fStopT;
+ const SkScalar fStartT,
+ fStopT;
+ const SkTrimPathEffect::Mode fMode;
typedef SkPathEffect INHERITED;
};
diff --git a/src/effects/SkTrimPathEffect.cpp b/src/effects/SkTrimPathEffect.cpp
index beaa36e17d..01e4a1c579 100644
--- a/src/effects/SkTrimPathEffect.cpp
+++ b/src/effects/SkTrimPathEffect.cpp
@@ -11,35 +11,90 @@
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
-SkTrimPE::SkTrimPE(SkScalar startT, SkScalar stopT) : fStartT(startT), fStopT(stopT) {
- SkASSERT(startT >= 0 && startT <= 1);
- SkASSERT(stopT >= 0 && stopT <= 1);
- SkASSERT(startT != stopT);
-}
+namespace {
+
+class Segmentator : public SkNoncopyable {
+public:
+ Segmentator(const SkPath& src, SkPath* dst)
+ : fMeasure(src, false)
+ , fDst(dst) {}
+
+ void add(SkScalar start, SkScalar stop) {
+ SkASSERT(start < stop);
+
+ // TODO: we appear to skip zero-length contours.
+ do {
+ const auto nextOffset = fCurrentSegmentOffset + fMeasure.getLength();
+
+ if (start < nextOffset) {
+ fMeasure.getSegment(start - fCurrentSegmentOffset,
+ stop - fCurrentSegmentOffset,
+ fDst, true);
+
+ if (stop < nextOffset)
+ break;
+ }
+
+ fCurrentSegmentOffset = nextOffset;
+ } while (fMeasure.nextContour());
+ }
+
+private:
+ SkPathMeasure fMeasure;
+ SkPath* fDst;
+
+ SkScalar fCurrentSegmentOffset = 0;
+
+ using INHERITED = SkNoncopyable;
+};
+
+} // namespace
+
+SkTrimPE::SkTrimPE(SkScalar startT, SkScalar stopT, SkTrimPathEffect::Mode mode)
+ : fStartT(startT), fStopT(stopT), fMode(mode) {}
bool SkTrimPE::filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
const SkRect* cullRect) const {
+ if (fStartT >= fStopT) {
+ SkASSERT(fMode == SkTrimPathEffect::Mode::kNormal);
+ return true;
+ }
+
+ // First pass: compute the total len.
+ SkScalar len = 0;
SkPathMeasure meas(src, false);
- SkScalar length = meas.getLength();
+ do {
+ len += meas.getLength();
+ } while (meas.nextContour());
- if (fStartT < fStopT) {
- meas.getSegment(fStartT * length, fStopT * length, dst, true);
+ const auto arcStart = len * fStartT,
+ arcStop = len * fStopT;
+
+ // Second pass: actually add segments.
+ Segmentator segmentator(src, dst);
+ if (fMode == SkTrimPathEffect::Mode::kNormal) {
+ if (arcStart < arcStop) segmentator.add(arcStart, arcStop);
} else {
- meas.getSegment(0, fStopT * length, dst, true);
- meas.getSegment(fStartT * length, length, dst, true);
+ if (0 < arcStart) segmentator.add(0, arcStart);
+ if (arcStop < len) segmentator.add(arcStop, len);
}
+
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) {
- SkScalar start = buffer.readScalar();
- SkScalar stop = buffer.readScalar();
- return SkTrimPathEffect::Make(start, stop);
+ 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);
}
#ifndef SK_IGNORE_TO_STRING
@@ -50,14 +105,21 @@ void SkTrimPE::toString(SkString* str) const {
//////////////////////////////////////////////////////////////////////////////////////////////////
-sk_sp<SkPathEffect> SkTrimPathEffect::Make(SkScalar startT, SkScalar stopT) {
+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) {
+
+ if (startT >= stopT && mode == Mode::kInverted) {
return nullptr;
}
- return sk_sp<SkPathEffect>(new SkTrimPE(startT, stopT));
+
+ return sk_sp<SkPathEffect>(new SkTrimPE(startT, stopT, mode));
}