blob: 7950d648afa645838ed9e1f6adeb050b30238739 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkDashPathEffect.h"
11#include "SkBuffer.h"
12#include "SkPathMeasure.h"
13
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000014static inline int is_even(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000015 return (~x) << 31;
16}
17
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000018static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase,
19 int32_t* index) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000020 int i;
21
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000022 for (i = 0; phase > intervals[i]; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000023 phase -= intervals[i];
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000024 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000025 *index = i;
26 return intervals[i] - phase;
27}
28
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000029SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
30 SkScalar phase, bool scaleToFit)
31 : fScaleToFit(scaleToFit) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000032 SkASSERT(intervals);
33 SkASSERT(count > 1 && SkAlign2(count) == count);
34
35 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count);
36 fCount = count;
37
38 SkScalar len = 0;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000039 for (int i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000040 SkASSERT(intervals[i] >= 0);
41 fIntervals[i] = intervals[i];
42 len += intervals[i];
43 }
44 fIntervalLength = len;
45
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000046 if (len > 0) { // we don't handle 0 length dash arrays
47 if (phase < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000048 phase = -phase;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000049 if (phase > len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000050 phase = SkScalarMod(phase, len);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000051 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000052 phase = len - phase;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000053 } else if (phase >= len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000054 phase = SkScalarMod(phase, len);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000055 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000056
reed@google.com9de4dc92011-07-15 14:14:45 +000057 // got to watch out for values that might make us go out of bounds
58 if (!SkScalarIsFinite(phase) || !SkScalarIsFinite(len)) {
59 goto BAD_DASH;
60 }
61
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 SkASSERT(phase >= 0 && phase < len);
63 fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex);
64
65 SkASSERT(fInitialDashLength >= 0);
66 SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000067 } else {
reed@google.com9de4dc92011-07-15 14:14:45 +000068 BAD_DASH:
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 fInitialDashLength = -1; // signal bad dash intervals
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000070 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000071}
72
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000073SkDashPathEffect::~SkDashPathEffect() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 sk_free(fIntervals);
75}
76
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000077bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
78 SkScalar* width) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 // we do nothing if the src wants to be filled, or if our dashlength is 0
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000080 if (*width < 0 || fInitialDashLength < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 return false;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000082 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000083
84 SkPathMeasure meas(src, false);
85 const SkScalar* intervals = fIntervals;
86
87 do {
88 bool skipFirstSegment = meas.isClosed();
89 bool addedSegment = false;
90 SkScalar length = meas.getLength();
91 int index = fInitialDashIndex;
92 SkScalar scale = SK_Scalar1;
93
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000094 if (fScaleToFit) {
95 if (fIntervalLength >= length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 scale = SkScalarDiv(length, fIntervalLength);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000097 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 SkScalar div = SkScalarDiv(length, fIntervalLength);
99 int n = SkScalarFloor(div);
100 scale = SkScalarDiv(length, n * fIntervalLength);
101 }
102 }
103
104 SkScalar distance = 0;
105 SkScalar dlen = SkScalarMul(fInitialDashLength, scale);
106
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000107 while (distance < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 SkASSERT(dlen >= 0);
109 addedSegment = false;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000110 if (is_even(index) && dlen > 0 && !skipFirstSegment) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 addedSegment = true;
112 meas.getSegment(distance, distance + dlen, dst, true);
113 }
114 distance += dlen;
115
116 // clear this so we only respect it the first time around
117 skipFirstSegment = false;
118
119 // wrap around our intervals array if necessary
120 index += 1;
121 SkASSERT(index <= fCount);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000122 if (index == fCount) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 index = 0;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000124 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125
126 // fetch our next dlen
127 dlen = SkScalarMul(intervals[index], scale);
128 }
129
130 // extend if we ended on a segment and we need to join up with the (skipped) initial segment
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000131 if (meas.isClosed() && is_even(fInitialDashIndex) &&
132 fInitialDashLength > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000134 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 } while (meas.nextContour());
136 return true;
137}
138
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000139SkFlattenable::Factory SkDashPathEffect::getFactory() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 return fInitialDashLength < 0 ? NULL : CreateProc;
141}
142
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000143void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 SkASSERT(fInitialDashLength >= 0);
145
146 buffer.write32(fCount);
147 buffer.write32(fInitialDashIndex);
148 buffer.writeScalar(fInitialDashLength);
149 buffer.writeScalar(fIntervalLength);
150 buffer.write32(fScaleToFit);
151 buffer.writeMul4(fIntervals, fCount * sizeof(fIntervals[0]));
152}
153
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000154SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 return SkNEW_ARGS(SkDashPathEffect, (buffer));
156}
157
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000158SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 fCount = buffer.readS32();
160 fInitialDashIndex = buffer.readS32();
161 fInitialDashLength = buffer.readScalar();
162 fIntervalLength = buffer.readScalar();
163 fScaleToFit = (buffer.readS32() != 0);
164
165 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
166 buffer.read(fIntervals, fCount * sizeof(fIntervals[0]));
167}
168
reed@google.com6bac9472011-06-21 19:24:00 +0000169///////////////////////////////////////////////////////////////////////////////
170
caryclark@google.comd26147a2011-12-15 14:16:43 +0000171SK_DEFINE_FLATTENABLE_REGISTRAR(SkDashPathEffect)