michael@0: michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: #include "SkParse.h" michael@0: #include "SkParsePath.h" michael@0: michael@0: static inline bool is_between(int c, int min, int max) { michael@0: return (unsigned)(c - min) <= (unsigned)(max - min); michael@0: } michael@0: michael@0: static inline bool is_ws(int c) { michael@0: return is_between(c, 1, 32); michael@0: } michael@0: michael@0: static inline bool is_digit(int c) { michael@0: return is_between(c, '0', '9'); michael@0: } michael@0: michael@0: static inline bool is_sep(int c) { michael@0: return is_ws(c) || c == ','; michael@0: } michael@0: michael@0: static inline bool is_lower(int c) { michael@0: return is_between(c, 'a', 'z'); michael@0: } michael@0: michael@0: static inline int to_upper(int c) { michael@0: return c - 'a' + 'A'; michael@0: } michael@0: michael@0: static const char* skip_ws(const char str[]) { michael@0: SkASSERT(str); michael@0: while (is_ws(*str)) michael@0: str++; michael@0: return str; michael@0: } michael@0: michael@0: static const char* skip_sep(const char str[]) { michael@0: SkASSERT(str); michael@0: while (is_sep(*str)) michael@0: str++; michael@0: return str; michael@0: } michael@0: michael@0: static const char* find_points(const char str[], SkPoint value[], int count, michael@0: bool isRelative, SkPoint* relative) { michael@0: str = SkParse::FindScalars(str, &value[0].fX, count * 2); michael@0: if (isRelative) { michael@0: for (int index = 0; index < count; index++) { michael@0: value[index].fX += relative->fX; michael@0: value[index].fY += relative->fY; michael@0: } michael@0: } michael@0: return str; michael@0: } michael@0: michael@0: static const char* find_scalar(const char str[], SkScalar* value, michael@0: bool isRelative, SkScalar relative) { michael@0: str = SkParse::FindScalar(str, value); michael@0: if (isRelative) { michael@0: *value += relative; michael@0: } michael@0: return str; michael@0: } michael@0: michael@0: bool SkParsePath::FromSVGString(const char data[], SkPath* result) { michael@0: SkPath path; michael@0: SkPoint f = {0, 0}; michael@0: SkPoint c = {0, 0}; michael@0: SkPoint lastc = {0, 0}; michael@0: SkPoint points[3]; michael@0: char op = '\0'; michael@0: char previousOp = '\0'; michael@0: bool relative = false; michael@0: for (;;) { michael@0: data = skip_ws(data); michael@0: if (data[0] == '\0') { michael@0: break; michael@0: } michael@0: char ch = data[0]; michael@0: if (is_digit(ch) || ch == '-' || ch == '+') { michael@0: if (op == '\0') { michael@0: return false; michael@0: } michael@0: } else { michael@0: op = ch; michael@0: relative = false; michael@0: if (is_lower(op)) { michael@0: op = (char) to_upper(op); michael@0: relative = true; michael@0: } michael@0: data++; michael@0: data = skip_sep(data); michael@0: } michael@0: switch (op) { michael@0: case 'M': michael@0: data = find_points(data, points, 1, relative, &c); michael@0: path.moveTo(points[0]); michael@0: op = 'L'; michael@0: c = points[0]; michael@0: break; michael@0: case 'L': michael@0: data = find_points(data, points, 1, relative, &c); michael@0: path.lineTo(points[0]); michael@0: c = points[0]; michael@0: break; michael@0: case 'H': { michael@0: SkScalar x; michael@0: data = find_scalar(data, &x, relative, c.fX); michael@0: path.lineTo(x, c.fY); michael@0: c.fX = x; michael@0: } break; michael@0: case 'V': { michael@0: SkScalar y; michael@0: data = find_scalar(data, &y, relative, c.fY); michael@0: path.lineTo(c.fX, y); michael@0: c.fY = y; michael@0: } break; michael@0: case 'C': michael@0: data = find_points(data, points, 3, relative, &c); michael@0: goto cubicCommon; michael@0: case 'S': michael@0: data = find_points(data, &points[1], 2, relative, &c); michael@0: points[0] = c; michael@0: if (previousOp == 'C' || previousOp == 'S') { michael@0: points[0].fX -= lastc.fX - c.fX; michael@0: points[0].fY -= lastc.fY - c.fY; michael@0: } michael@0: cubicCommon: michael@0: path.cubicTo(points[0], points[1], points[2]); michael@0: lastc = points[1]; michael@0: c = points[2]; michael@0: break; michael@0: case 'Q': // Quadratic Bezier Curve michael@0: data = find_points(data, points, 2, relative, &c); michael@0: goto quadraticCommon; michael@0: case 'T': michael@0: data = find_points(data, &points[1], 1, relative, &c); michael@0: points[0] = points[1]; michael@0: if (previousOp == 'Q' || previousOp == 'T') { michael@0: points[0].fX = c.fX * 2 - lastc.fX; michael@0: points[0].fY = c.fY * 2 - lastc.fY; michael@0: } michael@0: quadraticCommon: michael@0: path.quadTo(points[0], points[1]); michael@0: lastc = points[0]; michael@0: c = points[1]; michael@0: break; michael@0: case 'Z': michael@0: path.close(); michael@0: #if 0 // !!! still a bug? michael@0: if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) { michael@0: c.fX -= SkScalar.Epsilon; // !!! enough? michael@0: fPath.moveTo(c); michael@0: fPath.lineTo(f); michael@0: fPath.close(); michael@0: } michael@0: #endif michael@0: c = f; michael@0: op = '\0'; michael@0: break; michael@0: case '~': { michael@0: SkPoint args[2]; michael@0: data = find_points(data, args, 2, false, NULL); michael@0: path.moveTo(args[0].fX, args[0].fY); michael@0: path.lineTo(args[1].fX, args[1].fY); michael@0: } break; michael@0: default: michael@0: return false; michael@0: } michael@0: if (previousOp == 0) { michael@0: f = c; michael@0: } michael@0: previousOp = op; michael@0: } michael@0: // we're good, go ahead and swap in the result michael@0: result->swap(path); michael@0: return true; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #include "SkString.h" michael@0: #include "SkStream.h" michael@0: michael@0: static void write_scalar(SkWStream* stream, SkScalar value) { michael@0: char buffer[64]; michael@0: #ifdef SK_BUILD_FOR_WIN32 michael@0: int len = _snprintf(buffer, sizeof(buffer), "%g", value); michael@0: #else michael@0: int len = snprintf(buffer, sizeof(buffer), "%g", value); michael@0: #endif michael@0: char* stop = buffer + len; michael@0: stream->write(buffer, stop - buffer); michael@0: } michael@0: michael@0: static void append_scalars(SkWStream* stream, char verb, const SkScalar data[], michael@0: int count) { michael@0: stream->write(&verb, 1); michael@0: write_scalar(stream, data[0]); michael@0: for (int i = 1; i < count; i++) { michael@0: stream->write(" ", 1); michael@0: write_scalar(stream, data[i]); michael@0: } michael@0: } michael@0: michael@0: void SkParsePath::ToSVGString(const SkPath& path, SkString* str) { michael@0: SkDynamicMemoryWStream stream; michael@0: michael@0: SkPath::Iter iter(path, false); michael@0: SkPoint pts[4]; michael@0: michael@0: for (;;) { michael@0: switch (iter.next(pts, false)) { michael@0: case SkPath::kConic_Verb: michael@0: SkASSERT(0); michael@0: break; michael@0: case SkPath::kMove_Verb: michael@0: append_scalars(&stream, 'M', &pts[0].fX, 2); michael@0: break; michael@0: case SkPath::kLine_Verb: michael@0: append_scalars(&stream, 'L', &pts[1].fX, 2); michael@0: break; michael@0: case SkPath::kQuad_Verb: michael@0: append_scalars(&stream, 'Q', &pts[1].fX, 4); michael@0: break; michael@0: case SkPath::kCubic_Verb: michael@0: append_scalars(&stream, 'C', &pts[1].fX, 6); michael@0: break; michael@0: case SkPath::kClose_Verb: michael@0: stream.write("Z", 1); michael@0: break; michael@0: case SkPath::kDone_Verb: michael@0: str->resize(stream.getOffset()); michael@0: stream.copyTo(str->writable_str()); michael@0: return; michael@0: } michael@0: } michael@0: }