blob: f593b5700287d091a5888a017303aaabdbd419f3 [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
reed@google.com9de4dc92011-07-15 14:14:45 +000065 // got to watch out for values that might make us go out of bounds
66 if (!SkScalarIsFinite(phase) || !SkScalarIsFinite(len)) {
67 goto BAD_DASH;
68 }
69
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 SkASSERT(phase >= 0 && phase < len);
71 fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex);
72
73 SkASSERT(fInitialDashLength >= 0);
74 SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000075 } else {
reed@google.com9de4dc92011-07-15 14:14:45 +000076 BAD_DASH:
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 fInitialDashLength = -1; // signal bad dash intervals
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000078 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000079}
80
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000081SkDashPathEffect::~SkDashPathEffect() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 sk_free(fIntervals);
83}
84
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000085bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
86 SkScalar* width) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 // we do nothing if the src wants to be filled, or if our dashlength is 0
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000088 if (*width < 0 || fInitialDashLength < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 return false;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000090 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000091
92 SkPathMeasure meas(src, false);
93 const SkScalar* intervals = fIntervals;
94
95 do {
96 bool skipFirstSegment = meas.isClosed();
97 bool addedSegment = false;
98 SkScalar length = meas.getLength();
99 int index = fInitialDashIndex;
100 SkScalar scale = SK_Scalar1;
101
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000102 if (fScaleToFit) {
103 if (fIntervalLength >= length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 scale = SkScalarDiv(length, fIntervalLength);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000105 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 SkScalar div = SkScalarDiv(length, fIntervalLength);
107 int n = SkScalarFloor(div);
108 scale = SkScalarDiv(length, n * fIntervalLength);
109 }
110 }
111
112 SkScalar distance = 0;
113 SkScalar dlen = SkScalarMul(fInitialDashLength, scale);
114
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000115 while (distance < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 SkASSERT(dlen >= 0);
117 addedSegment = false;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000118 if (is_even(index) && dlen > 0 && !skipFirstSegment) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 addedSegment = true;
120 meas.getSegment(distance, distance + dlen, dst, true);
121 }
122 distance += dlen;
123
124 // clear this so we only respect it the first time around
125 skipFirstSegment = false;
126
127 // wrap around our intervals array if necessary
128 index += 1;
129 SkASSERT(index <= fCount);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000130 if (index == fCount) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 index = 0;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000132 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133
134 // fetch our next dlen
135 dlen = SkScalarMul(intervals[index], scale);
136 }
137
138 // 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 +0000139 if (meas.isClosed() && is_even(fInitialDashIndex) &&
140 fInitialDashLength > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141 meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000142 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 } while (meas.nextContour());
144 return true;
145}
146
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000147SkFlattenable::Factory SkDashPathEffect::getFactory() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148 return fInitialDashLength < 0 ? NULL : CreateProc;
149}
150
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000151void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 SkASSERT(fInitialDashLength >= 0);
153
154 buffer.write32(fCount);
155 buffer.write32(fInitialDashIndex);
156 buffer.writeScalar(fInitialDashLength);
157 buffer.writeScalar(fIntervalLength);
158 buffer.write32(fScaleToFit);
159 buffer.writeMul4(fIntervals, fCount * sizeof(fIntervals[0]));
160}
161
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000162SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 return SkNEW_ARGS(SkDashPathEffect, (buffer));
164}
165
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000166SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 fCount = buffer.readS32();
168 fInitialDashIndex = buffer.readS32();
169 fInitialDashLength = buffer.readScalar();
170 fIntervalLength = buffer.readScalar();
171 fScaleToFit = (buffer.readS32() != 0);
172
173 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
174 buffer.read(fIntervals, fCount * sizeof(fIntervals[0]));
175}
176
reed@google.com6bac9472011-06-21 19:24:00 +0000177///////////////////////////////////////////////////////////////////////////////
178
179static SkFlattenable::Registrar gReg("SkDashPathEffect",
180 SkDashPathEffect::CreateProc);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181