blob: 82f357c7c5156b56a47446f181d8ae0abf23d40d [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/effects/SkDashPathEffect.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkDashPathEffect.h"
19#include "SkBuffer.h"
20#include "SkPathMeasure.h"
21
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000022static inline int is_even(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000023 return (~x) << 31;
24}
25
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000026static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase,
27 int32_t* index) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000028 int i;
29
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000030 for (i = 0; phase > intervals[i]; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000031 phase -= intervals[i];
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000032 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000033 *index = i;
34 return intervals[i] - phase;
35}
36
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000037SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
38 SkScalar phase, bool scaleToFit)
39 : fScaleToFit(scaleToFit) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000040 SkASSERT(intervals);
41 SkASSERT(count > 1 && SkAlign2(count) == count);
42
43 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count);
44 fCount = count;
45
46 SkScalar len = 0;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000047 for (int i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000048 SkASSERT(intervals[i] >= 0);
49 fIntervals[i] = intervals[i];
50 len += intervals[i];
51 }
52 fIntervalLength = len;
53
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000054 if (len > 0) { // we don't handle 0 length dash arrays
55 if (phase < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000056 phase = -phase;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000057 if (phase > len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 phase = SkScalarMod(phase, len);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000059 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 phase = len - phase;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000061 } else if (phase >= len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 phase = SkScalarMod(phase, len);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000063 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000064
65 SkASSERT(phase >= 0 && phase < len);
66 fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex);
67
68 SkASSERT(fInitialDashLength >= 0);
69 SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000070 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 fInitialDashLength = -1; // signal bad dash intervals
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000072 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000073}
74
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000075SkDashPathEffect::~SkDashPathEffect() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 sk_free(fIntervals);
77}
78
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000079bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
80 SkScalar* width) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 // we do nothing if the src wants to be filled, or if our dashlength is 0
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000082 if (*width < 0 || fInitialDashLength < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 return false;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000084 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000085
86 SkPathMeasure meas(src, false);
87 const SkScalar* intervals = fIntervals;
88
89 do {
90 bool skipFirstSegment = meas.isClosed();
91 bool addedSegment = false;
92 SkScalar length = meas.getLength();
93 int index = fInitialDashIndex;
94 SkScalar scale = SK_Scalar1;
95
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000096 if (fScaleToFit) {
97 if (fIntervalLength >= length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 scale = SkScalarDiv(length, fIntervalLength);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000099 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 SkScalar div = SkScalarDiv(length, fIntervalLength);
101 int n = SkScalarFloor(div);
102 scale = SkScalarDiv(length, n * fIntervalLength);
103 }
104 }
105
106 SkScalar distance = 0;
107 SkScalar dlen = SkScalarMul(fInitialDashLength, scale);
108
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000109 while (distance < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 SkASSERT(dlen >= 0);
111 addedSegment = false;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000112 if (is_even(index) && dlen > 0 && !skipFirstSegment) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113 addedSegment = true;
114 meas.getSegment(distance, distance + dlen, dst, true);
115 }
116 distance += dlen;
117
118 // clear this so we only respect it the first time around
119 skipFirstSegment = false;
120
121 // wrap around our intervals array if necessary
122 index += 1;
123 SkASSERT(index <= fCount);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000124 if (index == fCount) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 index = 0;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000126 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127
128 // fetch our next dlen
129 dlen = SkScalarMul(intervals[index], scale);
130 }
131
132 // 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 +0000133 if (meas.isClosed() && is_even(fInitialDashIndex) &&
134 fInitialDashLength > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000136 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137 } while (meas.nextContour());
138 return true;
139}
140
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000141SkFlattenable::Factory SkDashPathEffect::getFactory() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 return fInitialDashLength < 0 ? NULL : CreateProc;
143}
144
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000145void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 SkASSERT(fInitialDashLength >= 0);
147
148 buffer.write32(fCount);
149 buffer.write32(fInitialDashIndex);
150 buffer.writeScalar(fInitialDashLength);
151 buffer.writeScalar(fIntervalLength);
152 buffer.write32(fScaleToFit);
153 buffer.writeMul4(fIntervals, fCount * sizeof(fIntervals[0]));
154}
155
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000156SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 return SkNEW_ARGS(SkDashPathEffect, (buffer));
158}
159
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000160SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 fCount = buffer.readS32();
162 fInitialDashIndex = buffer.readS32();
163 fInitialDashLength = buffer.readScalar();
164 fIntervalLength = buffer.readScalar();
165 fScaleToFit = (buffer.readS32() != 0);
166
167 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
168 buffer.read(fIntervals, fCount * sizeof(fIntervals[0]));
169}
170
171