• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* 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 
is_even(int x)22 static inline int is_even(int x)
23 {
24     return (~x) << 31;
25 }
26 
FindFirstInterval(const SkScalar intervals[],SkScalar phase,int32_t * index)27 static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase, int32_t* index)
28 {
29     int i;
30 
31     for (i = 0; phase > intervals[i]; i++)
32         phase -= intervals[i];
33     *index = i;
34     return intervals[i] - phase;
35 }
36 
SkDashPathEffect(const SkScalar intervals[],int count,SkScalar phase,bool scaleToFit)37 SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase, bool scaleToFit)
38     : fScaleToFit(scaleToFit)
39 {
40     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;
47     for (int i = 0; i < count; i++)
48     {
49         SkASSERT(intervals[i] >= 0);
50         fIntervals[i] = intervals[i];
51         len += intervals[i];
52     }
53     fIntervalLength = len;
54 
55     if (len > 0)    // we don't handle 0 length dash arrays
56     {
57         if (phase < 0)
58         {
59             phase = -phase;
60             if (phase > len)
61                 phase = SkScalarMod(phase, len);
62             phase = len - phase;
63         }
64         else if (phase >= len)
65             phase = SkScalarMod(phase, len);
66 
67         SkASSERT(phase >= 0 && phase < len);
68         fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex);
69 
70         SkASSERT(fInitialDashLength >= 0);
71         SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
72     }
73     else
74         fInitialDashLength = -1;    // signal bad dash intervals
75 }
76 
~SkDashPathEffect()77 SkDashPathEffect::~SkDashPathEffect()
78 {
79     sk_free(fIntervals);
80 }
81 
filterPath(SkPath * dst,const SkPath & src,SkScalar * width)82 bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
83 {
84     // we do nothing if the src wants to be filled, or if our dashlength is 0
85     if (*width < 0 || fInitialDashLength < 0)
86         return false;
87 
88     SkPathMeasure   meas(src, false);
89     const SkScalar* intervals = fIntervals;
90 
91     do {
92         bool        skipFirstSegment = meas.isClosed();
93         bool        addedSegment = false;
94         SkScalar    length = meas.getLength();
95         int         index = fInitialDashIndex;
96         SkScalar    scale = SK_Scalar1;
97 
98         if (fScaleToFit)
99         {
100             if (fIntervalLength >= length)
101                 scale = SkScalarDiv(length, fIntervalLength);
102             else
103             {
104                 SkScalar div = SkScalarDiv(length, fIntervalLength);
105                 int n = SkScalarFloor(div);
106                 scale = SkScalarDiv(length, n * fIntervalLength);
107             }
108         }
109 
110         SkScalar    distance = 0;
111         SkScalar    dlen = SkScalarMul(fInitialDashLength, scale);
112 
113         while (distance < length)
114         {
115             SkASSERT(dlen >= 0);
116             addedSegment = false;
117             if (is_even(index) && dlen > 0 && !skipFirstSegment)
118             {
119                 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);
130             if (index == fCount)
131                 index = 0;
132 
133             // fetch our next dlen
134             dlen = SkScalarMul(intervals[index], scale);
135         }
136 
137         // extend if we ended on a segment and we need to join up with the (skipped) initial segment
138         if (meas.isClosed() && is_even(fInitialDashIndex) && fInitialDashLength > 0)
139             meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
140     } while (meas.nextContour());
141     return true;
142 }
143 
getFactory()144 SkFlattenable::Factory SkDashPathEffect::getFactory()
145 {
146     return fInitialDashLength < 0 ? NULL : CreateProc;
147 }
148 
flatten(SkFlattenableWriteBuffer & buffer)149 void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer)
150 {
151     SkASSERT(fInitialDashLength >= 0);
152 
153     buffer.write32(fCount);
154     buffer.write32(fInitialDashIndex);
155     buffer.writeScalar(fInitialDashLength);
156     buffer.writeScalar(fIntervalLength);
157     buffer.write32(fScaleToFit);
158     buffer.writeMul4(fIntervals, fCount * sizeof(fIntervals[0]));
159 }
160 
CreateProc(SkFlattenableReadBuffer & buffer)161 SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer)
162 {
163     return SkNEW_ARGS(SkDashPathEffect, (buffer));
164 }
165 
SkDashPathEffect(SkFlattenableReadBuffer & buffer)166 SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer)
167 {
168     fCount = buffer.readS32();
169     fInitialDashIndex = buffer.readS32();
170     fInitialDashLength = buffer.readScalar();
171     fIntervalLength = buffer.readScalar();
172     fScaleToFit = (buffer.readS32() != 0);
173 
174     fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
175     buffer.read(fIntervals, fCount * sizeof(fIntervals[0]));
176 }
177 
178 
179