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