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 :
14 0 : static inline int is_even(int x) {
15 0 : return (~x) << 31;
16 : }
17 :
18 0 : static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase,
19 : int32_t* index) {
20 : int i;
21 :
22 0 : for (i = 0; phase > intervals[i]; i++) {
23 0 : phase -= intervals[i];
24 : }
25 0 : *index = i;
26 0 : return intervals[i] - phase;
27 : }
28 :
29 0 : SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
30 : SkScalar phase, bool scaleToFit)
31 0 : : fScaleToFit(scaleToFit) {
32 0 : SkASSERT(intervals);
33 0 : SkASSERT(count > 1 && SkAlign2(count) == count);
34 :
35 0 : fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count);
36 0 : fCount = count;
37 :
38 0 : SkScalar len = 0;
39 0 : for (int i = 0; i < count; i++) {
40 0 : SkASSERT(intervals[i] >= 0);
41 0 : fIntervals[i] = intervals[i];
42 0 : len += intervals[i];
43 : }
44 0 : fIntervalLength = len;
45 :
46 0 : if (len > 0) { // we don't handle 0 length dash arrays
47 0 : if (phase < 0) {
48 0 : phase = -phase;
49 0 : if (phase > len) {
50 0 : phase = SkScalarMod(phase, len);
51 : }
52 0 : phase = len - phase;
53 0 : } else if (phase >= len) {
54 0 : phase = SkScalarMod(phase, len);
55 : }
56 :
57 : // got to watch out for values that might make us go out of bounds
58 0 : if (!SkScalarIsFinite(phase) || !SkScalarIsFinite(len)) {
59 0 : goto BAD_DASH;
60 : }
61 :
62 0 : SkASSERT(phase >= 0 && phase < len);
63 0 : fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex);
64 :
65 0 : SkASSERT(fInitialDashLength >= 0);
66 0 : SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
67 : } else {
68 : BAD_DASH:
69 0 : fInitialDashLength = -1; // signal bad dash intervals
70 : }
71 0 : }
72 :
73 0 : SkDashPathEffect::~SkDashPathEffect() {
74 0 : sk_free(fIntervals);
75 0 : }
76 :
77 0 : 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 0 : if (*width < 0 || fInitialDashLength < 0) {
81 0 : return false;
82 : }
83 :
84 0 : SkPathMeasure meas(src, false);
85 0 : const SkScalar* intervals = fIntervals;
86 :
87 0 : do {
88 0 : bool skipFirstSegment = meas.isClosed();
89 0 : bool addedSegment = false;
90 0 : SkScalar length = meas.getLength();
91 0 : int index = fInitialDashIndex;
92 0 : SkScalar scale = SK_Scalar1;
93 :
94 0 : if (fScaleToFit) {
95 0 : if (fIntervalLength >= length) {
96 0 : scale = SkScalarDiv(length, fIntervalLength);
97 : } else {
98 0 : SkScalar div = SkScalarDiv(length, fIntervalLength);
99 0 : int n = SkScalarFloor(div);
100 0 : scale = SkScalarDiv(length, n * fIntervalLength);
101 : }
102 : }
103 :
104 0 : SkScalar distance = 0;
105 0 : SkScalar dlen = SkScalarMul(fInitialDashLength, scale);
106 :
107 0 : while (distance < length) {
108 0 : SkASSERT(dlen >= 0);
109 0 : addedSegment = false;
110 0 : if (is_even(index) && dlen > 0 && !skipFirstSegment) {
111 0 : addedSegment = true;
112 0 : meas.getSegment(distance, distance + dlen, dst, true);
113 : }
114 0 : distance += dlen;
115 :
116 : // clear this so we only respect it the first time around
117 0 : skipFirstSegment = false;
118 :
119 : // wrap around our intervals array if necessary
120 0 : index += 1;
121 0 : SkASSERT(index <= fCount);
122 0 : if (index == fCount) {
123 0 : index = 0;
124 : }
125 :
126 : // fetch our next dlen
127 0 : 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 0 : if (meas.isClosed() && is_even(fInitialDashIndex) &&
132 : fInitialDashLength > 0) {
133 0 : meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
134 : }
135 : } while (meas.nextContour());
136 0 : return true;
137 : }
138 :
139 0 : SkFlattenable::Factory SkDashPathEffect::getFactory() {
140 0 : return fInitialDashLength < 0 ? NULL : CreateProc;
141 : }
142 :
143 0 : void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
144 0 : SkASSERT(fInitialDashLength >= 0);
145 :
146 0 : buffer.write32(fCount);
147 0 : buffer.write32(fInitialDashIndex);
148 0 : buffer.writeScalar(fInitialDashLength);
149 0 : buffer.writeScalar(fIntervalLength);
150 0 : buffer.write32(fScaleToFit);
151 0 : buffer.writeMul4(fIntervals, fCount * sizeof(fIntervals[0]));
152 0 : }
153 :
154 0 : SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
155 0 : return SkNEW_ARGS(SkDashPathEffect, (buffer));
156 : }
157 :
158 0 : SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) {
159 0 : fCount = buffer.readS32();
160 0 : fInitialDashIndex = buffer.readS32();
161 0 : fInitialDashLength = buffer.readScalar();
162 0 : fIntervalLength = buffer.readScalar();
163 0 : fScaleToFit = (buffer.readS32() != 0);
164 :
165 0 : fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
166 0 : buffer.read(fIntervals, fCount * sizeof(fIntervals[0]));
167 0 : }
168 :
169 : ///////////////////////////////////////////////////////////////////////////////
170 :
171 2928 : SK_DEFINE_FLATTENABLE_REGISTRAR(SkDashPathEffect)
|