blob: df98436364c4a989e5753eb7419a497a88671f76 [file] [log] [blame]
reed@android.combbff1d52009-06-05 16:21:03 +00001#include "SkParse.h"
2#include "SkParsePath.h"
3
4static inline bool is_between(int c, int min, int max) {
5 return (unsigned)(c - min) <= (unsigned)(max - min);
6}
7
8static inline bool is_ws(int c) {
9 return is_between(c, 1, 32);
10}
11
12static inline bool is_digit(int c) {
13 return is_between(c, '0', '9');
14}
15
16static inline bool is_sep(int c) {
17 return is_ws(c) || c == ',';
18}
19
20static inline bool is_lower(int c) {
21 return is_between(c, 'a', 'z');
22}
23
24static inline int to_upper(int c) {
25 return c - 'a' + 'A';
26}
27
28static const char* skip_ws(const char str[]) {
29 SkASSERT(str);
30 while (is_ws(*str))
31 str++;
32 return str;
33}
34
35static const char* skip_sep(const char str[]) {
36 SkASSERT(str);
37 while (is_sep(*str))
38 str++;
39 return str;
40}
41
42static const char* find_points(const char str[], SkPoint value[], int count,
43 bool isRelative, SkPoint* relative) {
44 str = SkParse::FindScalars(str, &value[0].fX, count * 2);
45 if (isRelative) {
46 for (int index = 0; index < count; index++) {
47 value[index].fX += relative->fX;
48 value[index].fY += relative->fY;
49 }
50 }
51 return str;
52}
53
54static const char* find_scalar(const char str[], SkScalar* value,
55 bool isRelative, SkScalar relative) {
56 str = SkParse::FindScalar(str, value);
57 if (isRelative) {
58 *value += relative;
59 }
60 return str;
61}
62
63bool SkParsePath::FromSVGString(const char data[], SkPath* result) {
64 SkPath path;
65 SkPoint f = {0, 0};
66 SkPoint c = {0, 0};
67 SkPoint lastc = {0, 0};
68 SkPoint points[3];
69 char op = '\0';
70 char previousOp = '\0';
71 bool relative = false;
72 for (;;) {
73 data = skip_ws(data);
74 if (data[0] == '\0') {
75 break;
76 }
77 char ch = data[0];
78 if (is_digit(ch) || ch == '-' || ch == '+') {
79 if (op == '\0') {
80 return false;
81 }
82 } else {
83 op = ch;
84 relative = false;
85 if (is_lower(op)) {
86 op = (char) to_upper(op);
87 relative = true;
88 }
89 data++;
90 data = skip_sep(data);
91 }
92 switch (op) {
93 case 'M':
94 data = find_points(data, points, 1, relative, &c);
95 path.moveTo(points[0]);
96 op = 'L';
97 c = points[0];
98 break;
99 case 'L':
100 data = find_points(data, points, 1, relative, &c);
101 path.lineTo(points[0]);
102 c = points[0];
103 break;
104 case 'H': {
105 SkScalar x;
106 data = find_scalar(data, &x, relative, c.fX);
107 path.lineTo(x, c.fY);
108 c.fX = x;
109 } break;
110 case 'V': {
111 SkScalar y;
112 data = find_scalar(data, &y, relative, c.fY);
113 path.lineTo(c.fX, y);
114 c.fY = y;
115 } break;
116 case 'C':
117 data = find_points(data, points, 3, relative, &c);
118 goto cubicCommon;
119 case 'S':
120 data = find_points(data, &points[1], 2, relative, &c);
121 points[0] = c;
122 if (previousOp == 'C' || previousOp == 'S') {
123 points[0].fX -= lastc.fX - c.fX;
124 points[0].fY -= lastc.fY - c.fY;
125 }
126 cubicCommon:
127 path.cubicTo(points[0], points[1], points[2]);
128 lastc = points[1];
129 c = points[2];
130 break;
131 case 'Q': // Quadratic Bezier Curve
132 data = find_points(data, points, 2, relative, &c);
133 goto quadraticCommon;
134 case 'T':
135 data = find_points(data, &points[1], 1, relative, &c);
136 points[0] = points[1];
137 if (previousOp == 'Q' || previousOp == 'T') {
138 points[0].fX = c.fX * 2 - lastc.fX;
139 points[0].fY = c.fY * 2 - lastc.fY;
140 }
141 quadraticCommon:
142 path.quadTo(points[0], points[1]);
143 lastc = points[0];
144 c = points[1];
145 break;
146 case 'Z':
147 path.close();
148#if 0 // !!! still a bug?
149 if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) {
150 c.fX -= SkScalar.Epsilon; // !!! enough?
151 fPath.moveTo(c);
152 fPath.lineTo(f);
153 fPath.close();
154 }
155#endif
156 c = f;
157 op = '\0';
158 break;
159 case '~': {
160 SkPoint args[2];
161 data = find_points(data, args, 2, false, NULL);
162 path.moveTo(args[0].fX, args[0].fY);
163 path.lineTo(args[1].fX, args[1].fY);
164 } break;
165 default:
166 return false;
167 }
168 if (previousOp == 0) {
169 f = c;
170 }
171 previousOp = op;
172 }
173 // we're good, go ahead and swap in the result
174 result->swap(path);
175 return true;
176}
177
178///////////////////////////////////////////////////////////////////////////////
179
180#include "SkString.h"
181#include "SkStream.h"
182
183static void write_scalar(SkWStream* stream, SkScalar value) {
184 char buffer[SkStrAppendScalar_MaxSize];
185 char* stop = SkStrAppendScalar(buffer, value);
186 stream->write(buffer, stop - buffer);
187}
188
189static void append_scalars(SkWStream* stream, char verb, const SkScalar data[],
190 int count) {
191 stream->write(&verb, 1);
192 write_scalar(stream, data[0]);
193 for (int i = 1; i < count; i++) {
194 if (data[i] >= 0) {
195 // can skip the separater if data[i] is negative
196 stream->write(" ", 1);
197 }
198 write_scalar(stream, data[i]);
199 }
200}
201
202void SkParsePath::ToSVGString(const SkPath& path, SkString* str) {
203 SkDynamicMemoryWStream stream;
204
205 SkPath::Iter iter(path, false);
206 SkPoint pts[4];
207
208 for (;;) {
209 switch (iter.next(pts)) {
210 case SkPath::kMove_Verb:
211 append_scalars(&stream, 'M', &pts[0].fX, 2);
212 break;
213 case SkPath::kLine_Verb:
214 append_scalars(&stream, 'L', &pts[1].fX, 2);
215 break;
216 case SkPath::kQuad_Verb:
217 append_scalars(&stream, 'Q', &pts[1].fX, 4);
218 break;
219 case SkPath::kCubic_Verb:
220 append_scalars(&stream, 'C', &pts[1].fX, 6);
221 break;
222 case SkPath::kClose_Verb:
223 stream.write("Z", 1);
224 break;
225 case SkPath::kDone_Verb:
226 str->resize(stream.getOffset());
227 stream.copyTo(str->writable_str());
228 return;
229 }
230 }
231}
232