1
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
9
10 #include "SkDashPathEffect.h"
11 #include "SkBuffer.h"
12 #include "SkPathMeasure.h"
13
is_even(int x)14 static inline int is_even(int x) {
15 return (~x) << 31;
16 }
17
FindFirstInterval(const SkScalar intervals[],SkScalar phase,int32_t * index)18 static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase,
19 int32_t* index) {
20 int i;
21
22 for (i = 0; phase > intervals[i]; i++) {
23 phase -= intervals[i];
24 }
25 *index = i;
26 return intervals[i] - phase;
27 }
28
SkDashPathEffect(const SkScalar intervals[],int count,SkScalar phase,bool scaleToFit)29 SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
30 SkScalar phase, bool scaleToFit)
31 : fScaleToFit(scaleToFit) {
32 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;
39 for (int i = 0; i < count; i++) {
40 SkASSERT(intervals[i] >= 0);
41 fIntervals[i] = intervals[i];
42 len += intervals[i];
43 }
44 fIntervalLength = len;
45
46 if (len > 0) { // we don't handle 0 length dash arrays
47 if (phase < 0) {
48 phase = -phase;
49 if (phase > len) {
50 phase = SkScalarMod(phase, len);
51 }
52 phase = len - phase;
53 } else if (phase >= len) {
54 phase = SkScalarMod(phase, len);
55 }
56
57 // 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
62 SkASSERT(phase >= 0 && phase < len);
63 fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex);
64
65 SkASSERT(fInitialDashLength >= 0);
66 SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
67 } else {
68 BAD_DASH:
69 fInitialDashLength = -1; // signal bad dash intervals
70 }
71 }
72
~SkDashPathEffect()73 SkDashPathEffect::~SkDashPathEffect() {
74 sk_free(fIntervals);
75 }
76
filterPath(SkPath * dst,const SkPath & src,SkScalar * width)77 bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
78 SkScalar* width) {
79 // we do nothing if the src wants to be filled, or if our dashlength is 0
80 if (*width < 0 || fInitialDashLength < 0) {
81 return false;
82 }
83
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
94 if (fScaleToFit) {
95 if (fIntervalLength >= length) {
96 scale = SkScalarDiv(length, fIntervalLength);
97 } else {
98 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
107 while (distance < length) {
108 SkASSERT(dlen >= 0);
109 addedSegment = false;
110 if (is_even(index) && dlen > 0 && !skipFirstSegment) {
111 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);
122 if (index == fCount) {
123 index = 0;
124 }
125
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
131 if (meas.isClosed() && is_even(fInitialDashIndex) &&
132 fInitialDashLength > 0) {
133 meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
134 }
135 } while (meas.nextContour());
136 return true;
137 }
138
getFactory()139 SkFlattenable::Factory SkDashPathEffect::getFactory() {
140 return fInitialDashLength < 0 ? NULL : CreateProc;
141 }
142
flatten(SkFlattenableWriteBuffer & buffer)143 void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
144 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
CreateProc(SkFlattenableReadBuffer & buffer)154 SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
155 return SkNEW_ARGS(SkDashPathEffect, (buffer));
156 }
157
SkDashPathEffect(SkFlattenableReadBuffer & buffer)158 SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) {
159 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
169 ///////////////////////////////////////////////////////////////////////////////
170
171 SK_DEFINE_FLATTENABLE_REGISTRAR(SkDashPathEffect)
172