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