OLD | NEW |
| (Empty) |
1 /* libs/graphics/animator/SkSVGPath.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 <ctype.h> | |
19 #include "SkDrawPath.h" | |
20 #include "SkParse.h" | |
21 #include "SkPoint.h" | |
22 #include "SkUtils.h" | |
23 #define QUADRATIC_APPROXIMATION 1 | |
24 | |
25 #if QUADRATIC_APPROXIMATION | |
26 ////////////////////////////////////////////////////////////////////////////////
//// | |
27 //functions to approximate a cubic using two quadratics | |
28 | |
29 // midPt sets the first argument to be the midpoint of the other two | |
30 // it is used by quadApprox | |
31 static inline void midPt(SkPoint& dest,const SkPoint& a,const SkPoint& b) | |
32 { | |
33 dest.set(SkScalarAve(a.fX, b.fX),SkScalarAve(a.fY, b.fY)); | |
34 } | |
35 // quadApprox - makes an approximation, which we hope is faster | |
36 static void quadApprox(SkPath &fPath, const SkPoint &p0, const SkPoint &p1, cons
t SkPoint &p2) | |
37 { | |
38 //divide the cubic up into two cubics, then convert them into quadratics | |
39 //define our points | |
40 SkPoint c,j,k,l,m,n,o,p,q, mid; | |
41 fPath.getLastPt(&c); | |
42 midPt(j, p0, c); | |
43 midPt(k, p0, p1); | |
44 midPt(l, p1, p2); | |
45 midPt(o, j, k); | |
46 midPt(p, k, l); | |
47 midPt(q, o, p); | |
48 //compute the first half | |
49 m.set(SkScalarHalf(3*j.fX - c.fX), SkScalarHalf(3*j.fY - c.fY)); | |
50 n.set(SkScalarHalf(3*o.fX -q.fX), SkScalarHalf(3*o.fY - q.fY)); | |
51 midPt(mid,m,n); | |
52 fPath.quadTo(mid,q); | |
53 c = q; | |
54 //compute the second half | |
55 m.set(SkScalarHalf(3*p.fX - c.fX), SkScalarHalf(3*p.fY - c.fY)); | |
56 n.set(SkScalarHalf(3*l.fX -p2.fX),SkScalarHalf(3*l.fY -p2.fY)); | |
57 midPt(mid,m,n); | |
58 fPath.quadTo(mid,p2); | |
59 } | |
60 #endif | |
61 | |
62 | |
63 static inline bool is_between(int c, int min, int max) | |
64 { | |
65 return (unsigned)(c - min) <= (unsigned)(max - min); | |
66 } | |
67 | |
68 static inline bool is_ws(int c) | |
69 { | |
70 return is_between(c, 1, 32); | |
71 } | |
72 | |
73 static inline bool is_digit(int c) | |
74 { | |
75 return is_between(c, '0', '9'); | |
76 } | |
77 | |
78 static inline bool is_sep(int c) | |
79 { | |
80 return is_ws(c) || c == ','; | |
81 } | |
82 | |
83 static const char* skip_ws(const char str[]) | |
84 { | |
85 SkASSERT(str); | |
86 while (is_ws(*str)) | |
87 str++; | |
88 return str; | |
89 } | |
90 | |
91 static const char* skip_sep(const char str[]) | |
92 { | |
93 SkASSERT(str); | |
94 while (is_sep(*str)) | |
95 str++; | |
96 return str; | |
97 } | |
98 | |
99 static const char* find_points(const char str[], SkPoint value[], int count, | |
100 bool isRelative, SkPoint* relative) | |
101 { | |
102 str = SkParse::FindScalars(str, &value[0].fX, count * 2); | |
103 if (isRelative) { | |
104 for (int index = 0; index < count; index++) { | |
105 value[index].fX += relative->fX; | |
106 value[index].fY += relative->fY; | |
107 } | |
108 } | |
109 return str; | |
110 } | |
111 | |
112 static const char* find_scalar(const char str[], SkScalar* value, | |
113 bool isRelative, SkScalar relative) | |
114 { | |
115 str = SkParse::FindScalar(str, value); | |
116 if (isRelative) | |
117 *value += relative; | |
118 return str; | |
119 } | |
120 | |
121 void SkDrawPath::parseSVG() { | |
122 fPath.reset(); | |
123 const char* data = d.c_str(); | |
124 SkPoint f = {0, 0}; | |
125 SkPoint c = {0, 0}; | |
126 SkPoint lastc = {0, 0}; | |
127 SkPoint points[3]; | |
128 char op = '\0'; | |
129 char previousOp = '\0'; | |
130 bool relative = false; | |
131 do { | |
132 data = skip_ws(data); | |
133 if (data[0] == '\0') | |
134 break; | |
135 char ch = data[0]; | |
136 if (is_digit(ch) || ch == '-' || ch == '+') { | |
137 if (op == '\0') | |
138 return; | |
139 } | |
140 else { | |
141 op = ch; | |
142 relative = false; | |
143 if (islower(op)) { | |
144 op = (char) toupper(op); | |
145 relative = true; | |
146 } | |
147 data++; | |
148 data = skip_sep(data); | |
149 } | |
150 switch (op) { | |
151 case 'M': | |
152 data = find_points(data, points, 1, relative, &c); | |
153 fPath.moveTo(points[0]); | |
154 op = 'L'; | |
155 c = points[0]; | |
156 break; | |
157 case 'L': | |
158 data = find_points(data, points, 1, relative, &c); | |
159 fPath.lineTo(points[0]); | |
160 c = points[0]; | |
161 break; | |
162 case 'H': { | |
163 SkScalar x; | |
164 data = find_scalar(data, &x, relative, c.fX); | |
165 fPath.lineTo(x, c.fY); | |
166 c.fX = x; | |
167 } | |
168 break; | |
169 case 'V': { | |
170 SkScalar y; | |
171 data = find_scalar(data, &y, relative, c.fY); | |
172 fPath.lineTo(c.fX, y); | |
173 c.fY = y; | |
174 } | |
175 break; | |
176 case 'C': | |
177 data = find_points(data, points, 3, relative, &c); | |
178 goto cubicCommon; | |
179 case 'S': | |
180 data = find_points(data, &points[1], 2, relative, &c); | |
181 points[0] = c; | |
182 if (previousOp == 'C' || previousOp == 'S') { | |
183 points[0].fX -= lastc.fX - c.fX; | |
184 points[0].fY -= lastc.fY - c.fY; | |
185 } | |
186 cubicCommon: | |
187 // if (data[0] == '\0') | |
188 // return; | |
189 #if QUADRATIC_APPROXIMATION | |
190 quadApprox(fPath, points[0], points[1], points[2]); | |
191 #else //this way just does a boring, slow old cubic | |
192 fPath.cubicTo(points[0], points[1], points[2]); | |
193 #endif | |
194 //if we are using the quadApprox, lastc is what it would have been if we
had used | |
195 //cubicTo | |
196 lastc = points[1]; | |
197 c = points[2]; | |
198 break; | |
199 case 'Q': // Quadratic Bezier Curve | |
200 data = find_points(data, points, 2, relative, &c); | |
201 goto quadraticCommon; | |
202 case 'T': | |
203 data = find_points(data, &points[1], 1, relative, &c); | |
204 points[0] = points[1]; | |
205 if (previousOp == 'Q' || previousOp == 'T') { | |
206 points[0].fX = c.fX * 2 - lastc.fX; | |
207 points[0].fY = c.fY * 2 - lastc.fY; | |
208 } | |
209 quadraticCommon: | |
210 fPath.quadTo(points[0], points[1]); | |
211 lastc = points[0]; | |
212 c = points[1]; | |
213 break; | |
214 case 'Z': | |
215 fPath.close(); | |
216 #if 0 // !!! still a bug? | |
217 if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) { | |
218 c.fX -= SkScalar.Epsilon; // !!! enough? | |
219 fPath.moveTo(c); | |
220 fPath.lineTo(f); | |
221 fPath.close(); | |
222 } | |
223 #endif | |
224 c = f; | |
225 op = '\0'; | |
226 break; | |
227 case '~': { | |
228 SkPoint args[2]; | |
229 data = find_points(data, args, 2, false, NULL); | |
230 fPath.moveTo(args[0].fX, args[0].fY); | |
231 fPath.lineTo(args[1].fX, args[1].fY); | |
232 } | |
233 break; | |
234 default: | |
235 SkASSERT(0); | |
236 return; | |
237 } | |
238 if (previousOp == 0) | |
239 f = c; | |
240 previousOp = op; | |
241 } while (data[0] > 0); | |
242 } | |
243 | |
OLD | NEW |