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