1 
2 /*
3  * Copyright 2011 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 #include "SkParse.h"
9 #include "SkParsePath.h"
10 
is_between(int c,int min,int max)11 static inline bool is_between(int c, int min, int max) {
12     return (unsigned)(c - min) <= (unsigned)(max - min);
13 }
14 
is_ws(int c)15 static inline bool is_ws(int c) {
16     return is_between(c, 1, 32);
17 }
18 
is_digit(int c)19 static inline bool is_digit(int c) {
20     return is_between(c, '0', '9');
21 }
22 
is_sep(int c)23 static inline bool is_sep(int c) {
24     return is_ws(c) || c == ',';
25 }
26 
is_lower(int c)27 static inline bool is_lower(int c) {
28     return is_between(c, 'a', 'z');
29 }
30 
to_upper(int c)31 static inline int to_upper(int c) {
32     return c - 'a' + 'A';
33 }
34 
skip_ws(const char str[])35 static const char* skip_ws(const char str[]) {
36     SkASSERT(str);
37     while (is_ws(*str))
38         str++;
39     return str;
40 }
41 
skip_sep(const char str[])42 static const char* skip_sep(const char str[]) {
43     if (!str) {
44         return nullptr;
45     }
46     while (is_sep(*str))
47         str++;
48     return str;
49 }
50 
find_points(const char str[],SkPoint value[],int count,bool isRelative,SkPoint * relative)51 static const char* find_points(const char str[], SkPoint value[], int count,
52                                bool isRelative, SkPoint* relative) {
53     str = SkParse::FindScalars(str, &value[0].fX, count * 2);
54     if (isRelative) {
55         for (int index = 0; index < count; index++) {
56             value[index].fX += relative->fX;
57             value[index].fY += relative->fY;
58         }
59     }
60     return str;
61 }
62 
find_scalar(const char str[],SkScalar * value,bool isRelative,SkScalar relative)63 static const char* find_scalar(const char str[], SkScalar* value,
64                                bool isRelative, SkScalar relative) {
65     str = SkParse::FindScalar(str, value);
66     if (!str) {
67         return nullptr;
68     }
69     if (isRelative) {
70         *value += relative;
71     }
72     str = skip_sep(str);
73     return str;
74 }
75 
FromSVGString(const char data[],SkPath * result)76 bool SkParsePath::FromSVGString(const char data[], SkPath* result) {
77     SkPath path;
78     SkPoint first = {0, 0};
79     SkPoint c = {0, 0};
80     SkPoint lastc = {0, 0};
81     SkPoint points[3];
82     char op = '\0';
83     char previousOp = '\0';
84     bool relative = false;
85     for (;;) {
86         if (!data) {
87             // Truncated data
88             return false;
89         }
90         data = skip_ws(data);
91         if (data[0] == '\0') {
92             break;
93         }
94         char ch = data[0];
95         if (is_digit(ch) || ch == '-' || ch == '+') {
96             if (op == '\0') {
97                 return false;
98             }
99         } else if (is_sep(ch)) {
100             data = skip_sep(data);
101         } else {
102             op = ch;
103             relative = false;
104             if (is_lower(op)) {
105                 op = (char) to_upper(op);
106                 relative = true;
107             }
108             data++;
109             data = skip_sep(data);
110         }
111         switch (op) {
112             case 'M':
113                 data = find_points(data, points, 1, relative, &c);
114                 path.moveTo(points[0]);
115                 previousOp = '\0';
116                 op = 'L';
117                 c = points[0];
118                 break;
119             case 'L':
120                 data = find_points(data, points, 1, relative, &c);
121                 path.lineTo(points[0]);
122                 c = points[0];
123                 break;
124             case 'H': {
125                 SkScalar x;
126                 data = find_scalar(data, &x, relative, c.fX);
127                 path.lineTo(x, c.fY);
128                 c.fX = x;
129             } break;
130             case 'V': {
131                 SkScalar y;
132                 data = find_scalar(data, &y, relative, c.fY);
133                 path.lineTo(c.fX, y);
134                 c.fY = y;
135             } break;
136             case 'C':
137                 data = find_points(data, points, 3, relative, &c);
138                 goto cubicCommon;
139             case 'S':
140                 data = find_points(data, &points[1], 2, relative, &c);
141                 points[0] = c;
142                 if (previousOp == 'C' || previousOp == 'S') {
143                     points[0].fX -= lastc.fX - c.fX;
144                     points[0].fY -= lastc.fY - c.fY;
145                 }
146             cubicCommon:
147                 path.cubicTo(points[0], points[1], points[2]);
148                 lastc = points[1];
149                 c = points[2];
150                 break;
151             case 'Q':  // Quadratic Bezier Curve
152                 data = find_points(data, points, 2, relative, &c);
153                 goto quadraticCommon;
154             case 'T':
155                 data = find_points(data, &points[1], 1, relative, &c);
156                 points[0] = c;
157                 if (previousOp == 'Q' || previousOp == 'T') {
158                     points[0].fX -= lastc.fX - c.fX;
159                     points[0].fY -= lastc.fY - c.fY;
160                 }
161             quadraticCommon:
162                 path.quadTo(points[0], points[1]);
163                 lastc = points[0];
164                 c = points[1];
165                 break;
166             case 'A': {
167                 SkPoint radii;
168                 SkScalar angle, largeArc, sweep;
169                 if ((data = find_points(data, &radii, 1, false, nullptr))
170                         && (data = skip_sep(data))
171                         && (data = find_scalar(data, &angle, false, 0))
172                         && (data = skip_sep(data))
173                         && (data = find_scalar(data, &largeArc, false, 0))
174                         && (data = skip_sep(data))
175                         && (data = find_scalar(data, &sweep, false, 0))
176                         && (data = skip_sep(data))
177                         && (data = find_points(data, &points[0], 1, relative, &c))) {
178                     path.arcTo(radii, angle, (SkPath::ArcSize) SkToBool(largeArc),
179                             (SkPath::Direction) !SkToBool(sweep), points[0]);
180                     path.getLastPt(&c);
181                 }
182                 } break;
183             case 'Z':
184                 path.close();
185                 c = first;
186                 break;
187             case '~': {
188                 SkPoint args[2];
189                 data = find_points(data, args, 2, false, nullptr);
190                 path.moveTo(args[0].fX, args[0].fY);
191                 path.lineTo(args[1].fX, args[1].fY);
192             } break;
193             default:
194                 return false;
195         }
196         if (previousOp == 0) {
197             first = c;
198         }
199         previousOp = op;
200     }
201     // we're good, go ahead and swap in the result
202     result->swap(path);
203     return true;
204 }
205 
206 ///////////////////////////////////////////////////////////////////////////////
207 
208 #include "SkGeometry.h"
209 #include "SkString.h"
210 #include "SkStream.h"
211 
write_scalar(SkWStream * stream,SkScalar value)212 static void write_scalar(SkWStream* stream, SkScalar value) {
213     char buffer[64];
214 #ifdef SK_BUILD_FOR_WIN32
215     int len = _snprintf(buffer, sizeof(buffer), "%g", value);
216 #else
217     int len = snprintf(buffer, sizeof(buffer), "%g", value);
218 #endif
219     char* stop = buffer + len;
220     stream->write(buffer, stop - buffer);
221 }
222 
append_scalars(SkWStream * stream,char verb,const SkScalar data[],int count)223 static void append_scalars(SkWStream* stream, char verb, const SkScalar data[],
224                            int count) {
225     stream->write(&verb, 1);
226     write_scalar(stream, data[0]);
227     for (int i = 1; i < count; i++) {
228         stream->write(" ", 1);
229         write_scalar(stream, data[i]);
230     }
231 }
232 
ToSVGString(const SkPath & path,SkString * str)233 void SkParsePath::ToSVGString(const SkPath& path, SkString* str) {
234     SkDynamicMemoryWStream  stream;
235 
236     SkPath::Iter    iter(path, false);
237     SkPoint         pts[4];
238 
239     for (;;) {
240         switch (iter.next(pts, false)) {
241             case SkPath::kConic_Verb: {
242                 const SkScalar tol = SK_Scalar1 / 1024; // how close to a quad
243                 SkAutoConicToQuads quadder;
244                 const SkPoint* quadPts = quadder.computeQuads(pts, iter.conicWeight(), tol);
245                 for (int i = 0; i < quadder.countQuads(); ++i) {
246                     append_scalars(&stream, 'Q', &quadPts[i*2 + 1].fX, 4);
247                 }
248             } break;
249            case SkPath::kMove_Verb:
250                 append_scalars(&stream, 'M', &pts[0].fX, 2);
251                 break;
252             case SkPath::kLine_Verb:
253                 append_scalars(&stream, 'L', &pts[1].fX, 2);
254                 break;
255             case SkPath::kQuad_Verb:
256                 append_scalars(&stream, 'Q', &pts[1].fX, 4);
257                 break;
258             case SkPath::kCubic_Verb:
259                 append_scalars(&stream, 'C', &pts[1].fX, 6);
260                 break;
261             case SkPath::kClose_Verb:
262                 stream.write("Z", 1);
263                 break;
264             case SkPath::kDone_Verb:
265                 str->resize(stream.getOffset());
266                 stream.copyTo(str->writable_str());
267             return;
268         }
269     }
270 }
271