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 11 static inline bool is_between(int c, int min, int max) { 12 return (unsigned)(c - min) <= (unsigned)(max - min); 13 } 14 15 static inline bool is_ws(int c) { 16 return is_between(c, 1, 32); 17 } 18 19 static inline bool is_digit(int c) { 20 return is_between(c, '0', '9'); 21 } 22 23 static inline bool is_sep(int c) { 24 return is_ws(c) || c == ','; 25 } 26 27 static inline bool is_lower(int c) { 28 return is_between(c, 'a', 'z'); 29 } 30 31 static inline int to_upper(int c) { 32 return c - 'a' + 'A'; 33 } 34 35 static const char* skip_ws(const char str[]) { 36 SkASSERT(str); 37 while (is_ws(*str)) 38 str++; 39 return str; 40 } 41 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 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 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 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 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 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 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