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