add SkParsePath to go to/from SVG strings (e.g. "M0,0 L10,20")



git-svn-id: http://skia.googlecode.com/svn/trunk@203 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/Makefile b/Makefile
index 1d846a0..7e12a14 100644
--- a/Makefile
+++ b/Makefile
@@ -106,13 +106,8 @@
 # we let tests cheat and see private headers, so we can unittest modules
 C_INCLUDES += -Isrc/core
 
-TESTS_SRCS := GeometryTest.cpp MathTest.cpp MatrixTest.cpp PackBitsTest.cpp \
-              Sk64Test.cpp StringTest.cpp Test.cpp UtilsTest.cpp PathTest.cpp \
-              ClipCubicTest.cpp SrcOverTest.cpp StreamTest.cpp SortTest.cpp \
-			  BitmapCopyTest.cpp PathMeasureTest.cpp TriangulationTest.cpp \
-              TestSize.cpp testmain.cpp
-
-TESTS_SRCS := $(addprefix tests/, $(TESTS_SRCS))
+include tests/tests_files.mk
+TESTS_SRCS := $(addprefix tests/, $(SOURCE))
 
 TESTS_OBJS := $(TESTS_SRCS:.cpp=.o)
 TESTS_OBJS := $(addprefix out/, $(TESTS_OBJS))
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index c7289e8..fc323d3 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -562,10 +562,6 @@
     */
     void subdivide(SkScalar dist, bool bendLines, SkPath* dst = NULL) const;
 
-    /** Return an SVG-compatible string of the path.
-    */
-    void toString(SkString*) const;
-
     SkDEBUGCODE(void validate() const;)
 
 private:
diff --git a/include/utils/SkParsePath.h b/include/utils/SkParsePath.h
new file mode 100644
index 0000000..d271f7e
--- /dev/null
+++ b/include/utils/SkParsePath.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkParsePath_DEFINED
+#define SkParsePath_DEFINED
+
+#include "SkPath.h"
+
+class SkString;
+
+class SkParsePath {
+public:
+    static bool FromSVGString(const char str[], SkPath*);
+    static void ToSVGString(const SkPath&, SkString*);
+};
+
+#endif
+
diff --git a/samplecode/SampleArc.cpp b/samplecode/SampleArc.cpp
index ada1d0a..9deec81 100644
--- a/samplecode/SampleArc.cpp
+++ b/samplecode/SampleArc.cpp
@@ -16,10 +16,24 @@
 #include "SkPorterDuff.h"
 #include "SkLayerRasterizer.h"
 
+#include "SkParsePath.h"
+static void testparse() {
+    SkRect r;
+    r.set(0, 0, SkFloatToScalar(10), SkFloatToScalar(10.5));
+    SkPath p, p2;
+    SkString str, str2;
+
+    p.addRect(r);
+    SkParsePath::ToSVGString(p, &str);
+    SkParsePath::FromSVGString(str.c_str(), &p2);
+    SkParsePath::ToSVGString(p2, &str2);
+}
+
 class ArcsView : public SkView {
 public:
 	ArcsView()
     {
+        testparse();
         fSweep = SkIntToScalar(100);
     }
 
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 2127bb4..082d2e8 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -1220,61 +1220,6 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-
-#include "SkString.h"
-#include "SkStream.h"
-
-static void write_scalar(SkWStream* stream, SkScalar value) {
-    char    buffer[SkStrAppendScalar_MaxSize];
-    char*   stop = SkStrAppendScalar(buffer, value);
-    stream->write(buffer, stop - buffer);
-}
-
-static void append_scalars(SkWStream* stream, char verb, const SkScalar data[],
-                           int count) {
-    stream->write(&verb, 1);
-    write_scalar(stream, data[0]);
-    for (int i = 1; i < count; i++) {
-        if (data[i] >= 0) {
-            // can skip the separater if data[i] is negative
-            stream->write(" ", 1);
-        }
-        write_scalar(stream, data[i]);
-    }
-}
-
-void SkPath::toString(SkString* str) const {
-    SkDynamicMemoryWStream  stream;
-
-    SkPath::Iter    iter(*this, false);
-    SkPoint         pts[4];
-    
-    for (;;) {
-        switch (iter.next(pts)) {
-            case SkPath::kMove_Verb:
-                append_scalars(&stream, 'M', &pts[0].fX, 2);
-                break;
-            case SkPath::kLine_Verb:
-                append_scalars(&stream, 'L', &pts[1].fX, 2);
-                break;
-            case SkPath::kQuad_Verb:
-                append_scalars(&stream, 'Q', &pts[1].fX, 4);
-                break;
-            case SkPath::kCubic_Verb:
-                append_scalars(&stream, 'C', &pts[1].fX, 6);
-                break;
-            case SkPath::kClose_Verb:
-                stream.write("Z", 1);
-                break;
-            case SkPath::kDone_Verb:
-                str->resize(stream.getOffset());
-                stream.copyTo(str->writable_str());
-                return;
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
 #ifdef SK_DEBUG
diff --git a/src/xml/SkParse.cpp b/src/utils/SkParse.cpp
similarity index 100%
rename from src/xml/SkParse.cpp
rename to src/utils/SkParse.cpp
diff --git a/src/xml/SkParseColor.cpp b/src/utils/SkParseColor.cpp
similarity index 100%
rename from src/xml/SkParseColor.cpp
rename to src/utils/SkParseColor.cpp
diff --git a/src/utils/SkParsePath.cpp b/src/utils/SkParsePath.cpp
new file mode 100644
index 0000000..df98436
--- /dev/null
+++ b/src/utils/SkParsePath.cpp
@@ -0,0 +1,232 @@
+#include "SkParse.h"
+#include "SkParsePath.h"
+
+static inline bool is_between(int c, int min, int max) {
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_ws(int c) {
+    return is_between(c, 1, 32);
+}
+
+static inline bool is_digit(int c) {
+    return is_between(c, '0', '9');
+}
+
+static inline bool is_sep(int c) {
+    return is_ws(c) || c == ',';
+}
+
+static inline bool is_lower(int c) {
+    return is_between(c, 'a', 'z');
+}
+
+static inline int to_upper(int c) {
+    return c - 'a' + 'A';
+}
+
+static const char* skip_ws(const char str[]) {
+    SkASSERT(str);
+    while (is_ws(*str))
+        str++;
+    return str;
+}
+
+static const char* skip_sep(const char str[]) {
+    SkASSERT(str);
+    while (is_sep(*str))
+        str++;
+    return str;
+}
+
+static const char* find_points(const char str[], SkPoint value[], int count,
+                               bool isRelative, SkPoint* relative) {
+    str = SkParse::FindScalars(str, &value[0].fX, count * 2);
+    if (isRelative) {
+        for (int index = 0; index < count; index++) {
+            value[index].fX += relative->fX;
+            value[index].fY += relative->fY;
+        }
+    }
+    return str;
+}
+
+static const char* find_scalar(const char str[], SkScalar* value, 
+                               bool isRelative, SkScalar relative) {
+    str = SkParse::FindScalar(str, value);
+    if (isRelative) {
+        *value += relative;
+    }
+    return str;
+}
+
+bool SkParsePath::FromSVGString(const char data[], SkPath* result) {
+    SkPath path;
+    SkPoint f = {0, 0};
+    SkPoint c = {0, 0};
+    SkPoint lastc = {0, 0};
+    SkPoint points[3];
+    char op = '\0';
+    char previousOp = '\0';
+    bool relative = false;
+    for (;;) {
+        data = skip_ws(data);
+        if (data[0] == '\0') {
+            break;
+        }
+        char ch = data[0];
+        if (is_digit(ch) || ch == '-' || ch == '+') {
+            if (op == '\0') {
+                return false;
+            }
+        } else {
+            op = ch;
+            relative = false;
+            if (is_lower(op)) {
+                op = (char) to_upper(op);
+                relative = true;
+            }
+            data++;
+            data = skip_sep(data);
+        }
+        switch (op) {
+            case 'M':
+                data = find_points(data, points, 1, relative, &c);
+                path.moveTo(points[0]);
+                op = 'L';
+                c = points[0];
+                break;
+            case 'L': 
+                data = find_points(data, points, 1, relative, &c);
+                path.lineTo(points[0]);
+                c = points[0];
+                break;
+            case 'H': {
+                SkScalar x;
+                data = find_scalar(data, &x, relative, c.fX);
+                path.lineTo(x, c.fY);
+                c.fX = x;
+            } break;
+            case 'V': {
+                SkScalar y;
+                data = find_scalar(data, &y, relative, c.fY);
+                path.lineTo(c.fX, y);
+                c.fY = y;
+            } break;
+            case 'C': 
+                data = find_points(data, points, 3, relative, &c);
+                goto cubicCommon;
+            case 'S': 
+                data = find_points(data, &points[1], 2, relative, &c);
+                points[0] = c;
+                if (previousOp == 'C' || previousOp == 'S') {
+                    points[0].fX -= lastc.fX - c.fX;
+                    points[0].fY -= lastc.fY - c.fY;
+                }
+            cubicCommon:
+                path.cubicTo(points[0], points[1], points[2]);
+                lastc = points[1];
+                c = points[2];
+                break;
+            case 'Q':  // Quadratic Bezier Curve
+                data = find_points(data, points, 2, relative, &c);
+                goto quadraticCommon;
+            case 'T':
+                data = find_points(data, &points[1], 1, relative, &c);
+                points[0] = points[1];
+                if (previousOp == 'Q' || previousOp == 'T') {
+                    points[0].fX = c.fX * 2 - lastc.fX;
+                    points[0].fY = c.fY * 2 - lastc.fY;
+                }
+            quadraticCommon:
+                path.quadTo(points[0], points[1]);
+                lastc = points[0];
+                c = points[1];
+                break;
+            case 'Z':
+                path.close();
+#if 0   // !!! still a bug?
+                if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) {
+                    c.fX -= SkScalar.Epsilon;   // !!! enough?
+                    fPath.moveTo(c);
+                    fPath.lineTo(f);
+                    fPath.close();
+                }
+#endif
+                c = f;
+                op = '\0';
+                break;
+            case '~': {
+                SkPoint args[2];
+                data = find_points(data, args, 2, false, NULL);
+                path.moveTo(args[0].fX, args[0].fY);
+                path.lineTo(args[1].fX, args[1].fY);
+            } break;
+            default:
+                return false;
+        }
+        if (previousOp == 0) {
+            f = c;
+        }
+        previousOp = op;
+    }
+    // we're good, go ahead and swap in the result
+    result->swap(path);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkString.h"
+#include "SkStream.h"
+
+static void write_scalar(SkWStream* stream, SkScalar value) {
+    char    buffer[SkStrAppendScalar_MaxSize];
+    char*   stop = SkStrAppendScalar(buffer, value);
+    stream->write(buffer, stop - buffer);
+}
+
+static void append_scalars(SkWStream* stream, char verb, const SkScalar data[],
+                           int count) {
+    stream->write(&verb, 1);
+    write_scalar(stream, data[0]);
+    for (int i = 1; i < count; i++) {
+        if (data[i] >= 0) {
+            // can skip the separater if data[i] is negative
+            stream->write(" ", 1);
+        }
+        write_scalar(stream, data[i]);
+    }
+}
+
+void SkParsePath::ToSVGString(const SkPath& path, SkString* str) {
+    SkDynamicMemoryWStream  stream;
+
+    SkPath::Iter    iter(path, false);
+    SkPoint         pts[4];
+
+    for (;;) {
+        switch (iter.next(pts)) {
+            case SkPath::kMove_Verb:
+                append_scalars(&stream, 'M', &pts[0].fX, 2);
+                break;
+            case SkPath::kLine_Verb:
+                append_scalars(&stream, 'L', &pts[1].fX, 2);
+                break;
+            case SkPath::kQuad_Verb:
+                append_scalars(&stream, 'Q', &pts[1].fX, 4);
+                break;
+            case SkPath::kCubic_Verb:
+                append_scalars(&stream, 'C', &pts[1].fX, 6);
+                break;
+            case SkPath::kClose_Verb:
+                stream.write("Z", 1);
+                break;
+            case SkPath::kDone_Verb:
+                str->resize(stream.getOffset());
+                stream.copyTo(str->writable_str());
+            return;
+        }
+    }
+}
+
diff --git a/src/utils/utils_files.mk b/src/utils/utils_files.mk
index a8c9496..a744af0 100644
--- a/src/utils/utils_files.mk
+++ b/src/utils/utils_files.mk
@@ -5,6 +5,9 @@
 	SkDumpCanvas.cpp \
 	SkInterpolator.cpp \
 	SkNinePatch.cpp \
+    SkParse.cpp \
+    SkParseColor.cpp \
+    SkParsePath.cpp \
 	SkProxyCanvas.cpp \
 	SkSfntUtils.cpp \
 	SkUnitMappers.cpp
diff --git a/tests/ParsePathTest.cpp b/tests/ParsePathTest.cpp
new file mode 100644
index 0000000..917b0cb
--- /dev/null
+++ b/tests/ParsePathTest.cpp
@@ -0,0 +1,58 @@
+#include "Test.h"
+#include "SkParsePath.h"
+
+static void test_to_from(skiatest::Reporter* reporter, const SkPath& path) {
+    SkString str, str2;
+    SkParsePath::ToSVGString(path, &str);
+
+    SkPath path2;
+    bool success = SkParsePath::FromSVGString(str.c_str(), &path2);
+    REPORTER_ASSERT(reporter, success);
+
+    SkParsePath::ToSVGString(path2, &str2);
+    REPORTER_ASSERT(reporter, str == str2);
+#if 0 // closed paths are not equal, the iter explicitly gives the closing
+      // edge, even if it is not in the path.
+    REPORTER_ASSERT(reporter, path == path2);
+    if (path != path2) {
+        SkDebugf("str1=%s\nstr2=%s\n", str.c_str(), str2.c_str());
+    }
+#endif
+}
+
+static void TestParsePath(skiatest::Reporter* reporter) {
+    static const struct {
+        const char* fStr;
+        SkRect      fBounds;
+    } gRec[] = {
+        { "", { 0, 0, 0, 0 } },
+        { "M0,0L10,10", { 0, 0, SkIntToScalar(10), SkIntToScalar(10) } },
+        { "M-5.5,-0.5 Q 0 0 6,6.50",
+            { SkFloatToScalar(-5.5f), SkFloatToScalar(-0.5f),
+              SkFloatToScalar(6), SkFloatToScalar(6.5f) } }
+    };
+    
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+        SkPath  path;
+        bool success = SkParsePath::FromSVGString(gRec[i].fStr, &path);
+        REPORTER_ASSERT(reporter, success);
+        const SkRect& expectedBounds = gRec[i].fBounds;
+        const SkRect& pathBounds = path.getBounds();
+        REPORTER_ASSERT(reporter, expectedBounds == pathBounds);
+
+        test_to_from(reporter, path);
+    }
+    
+    SkRect r;
+    r.set(0, 0, SkFloatToScalar(10), SkFloatToScalar(10.5));
+    SkPath p;
+    p.addRect(r);
+    test_to_from(reporter, p);
+    p.addOval(r);
+    test_to_from(reporter, p);
+    p.addRoundRect(r, SkFloatToScalar(4), SkFloatToScalar(4.5));
+    test_to_from(reporter, p);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("ParsePath", ParsePathClass, TestParsePath)
diff --git a/tests/tests_files.mk b/tests/tests_files.mk
new file mode 100644
index 0000000..bce5be7
--- /dev/null
+++ b/tests/tests_files.mk
@@ -0,0 +1,20 @@
+SOURCE := \
+	GeometryTest.cpp \
+    MathTest.cpp \
+    MatrixTest.cpp \
+    PackBitsTest.cpp \
+    Sk64Test.cpp \
+    StringTest.cpp \
+    Test.cpp \
+    UtilsTest.cpp \
+    ParsePathTest.cpp \
+    PathTest.cpp \
+    ClipCubicTest.cpp \
+    SrcOverTest.cpp \
+    StreamTest.cpp \
+    SortTest.cpp \
+    BitmapCopyTest.cpp \
+    PathMeasureTest.cpp \
+    TriangulationTest.cpp \
+    TestSize.cpp \
+    testmain.cpp
diff --git a/xcode/effects/effects.xcodeproj/project.pbxproj b/xcode/effects/effects.xcodeproj/project.pbxproj
index 00d0a69..ed81d52 100644
--- a/xcode/effects/effects.xcodeproj/project.pbxproj
+++ b/xcode/effects/effects.xcodeproj/project.pbxproj
@@ -7,6 +7,9 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		000A996E0FD97113007E45BD /* SkParse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 000A996B0FD97113007E45BD /* SkParse.cpp */; };
+		000A996F0FD97113007E45BD /* SkParseColor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 000A996C0FD97113007E45BD /* SkParseColor.cpp */; };
+		000A99700FD97113007E45BD /* SkParsePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 000A996D0FD97113007E45BD /* SkParsePath.cpp */; };
 		0028867A0EFAE7500083E387 /* Sk1DPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 002886620EFAE7500083E387 /* Sk1DPathEffect.cpp */; };
 		0028867B0EFAE7500083E387 /* Sk2DPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 002886630EFAE7500083E387 /* Sk2DPathEffect.cpp */; };
 		0028867C0EFAE7500083E387 /* SkAvoidXfermode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 002886640EFAE7500083E387 /* SkAvoidXfermode.cpp */; };
@@ -45,6 +48,9 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
+		000A996B0FD97113007E45BD /* SkParse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkParse.cpp; path = ../../src/utils/SkParse.cpp; sourceTree = SOURCE_ROOT; };
+		000A996C0FD97113007E45BD /* SkParseColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkParseColor.cpp; path = ../../src/utils/SkParseColor.cpp; sourceTree = SOURCE_ROOT; };
+		000A996D0FD97113007E45BD /* SkParsePath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkParsePath.cpp; path = ../../src/utils/SkParsePath.cpp; sourceTree = SOURCE_ROOT; };
 		002886620EFAE7500083E387 /* Sk1DPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sk1DPathEffect.cpp; path = ../../src/effects/Sk1DPathEffect.cpp; sourceTree = SOURCE_ROOT; };
 		002886630EFAE7500083E387 /* Sk2DPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sk2DPathEffect.cpp; path = ../../src/effects/Sk2DPathEffect.cpp; sourceTree = SOURCE_ROOT; };
 		002886640EFAE7500083E387 /* SkAvoidXfermode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkAvoidXfermode.cpp; path = ../../src/effects/SkAvoidXfermode.cpp; sourceTree = SOURCE_ROOT; };
@@ -97,6 +103,9 @@
 		002886D50EFAEA350083E387 /* utils */ = {
 			isa = PBXGroup;
 			children = (
+				000A996B0FD97113007E45BD /* SkParse.cpp */,
+				000A996C0FD97113007E45BD /* SkParseColor.cpp */,
+				000A996D0FD97113007E45BD /* SkParsePath.cpp */,
 				002886C50EFAEA260083E387 /* SkCamera.cpp */,
 				002886C60EFAEA260083E387 /* SkColorMatrix.cpp */,
 				002886C70EFAEA260083E387 /* SkCullPoints.cpp */,
@@ -256,6 +265,9 @@
 				2762F61D0FCCC92C002BD8B4 /* SkGroupShape.cpp in Sources */,
 				2762F61E0FCCC92C002BD8B4 /* SkNWayCanvas.cpp in Sources */,
 				2762F61F0FCCC92C002BD8B4 /* SkRectShape.cpp in Sources */,
+				000A996E0FD97113007E45BD /* SkParse.cpp in Sources */,
+				000A996F0FD97113007E45BD /* SkParseColor.cpp in Sources */,
+				000A99700FD97113007E45BD /* SkParsePath.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
index fb0cd3b..d3679c4 100644
--- a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
+++ b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
@@ -22,10 +22,9 @@
 		00003C820EFC22E1000FF73A /* SkOSWindow_Mac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C7F0EFC22E1000FF73A /* SkOSWindow_Mac.cpp */; };
 		00003C950EFC2316000FF73A /* libeffects.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00003C940EFC230E000FF73A /* libeffects.a */; };
 		00003C9E0EFC233F000FF73A /* SkDOM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C9A0EFC233F000FF73A /* SkDOM.cpp */; };
-		00003C9F0EFC233F000FF73A /* SkParse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C9B0EFC233F000FF73A /* SkParse.cpp */; };
-		00003CA00EFC233F000FF73A /* SkParseColor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C9C0EFC233F000FF73A /* SkParseColor.cpp */; };
 		00003CA10EFC233F000FF73A /* SkXMLParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C9D0EFC233F000FF73A /* SkXMLParser.cpp */; };
 		00003CA40EFC235F000FF73A /* SkXMLParser_empty.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003CA30EFC235F000FF73A /* SkXMLParser_empty.cpp */; };
+		000A99820FD97526007E45BD /* SampleArc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A41E4A0EFC312F00C9CBEB /* SampleArc.cpp */; };
 		0028847B0EFAB46A0083E387 /* libcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 002884510EFAA35C0083E387 /* libcore.a */; };
 		002884BD0EFAB6A30083E387 /* libmaccore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 002884BC0EFAB69F0083E387 /* libmaccore.a */; };
 		003145320FB9B48F00B10956 /* SampleShapes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 003145310FB9B48F00B10956 /* SampleShapes.cpp */; };
@@ -69,7 +68,6 @@
 		008C4D980F77DAEE0056981C /* SampleHairline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 008C4D970F77DAEE0056981C /* SampleHairline.cpp */; };
 		009490320FB0A5B90063C792 /* SampleLayerMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 009490310FB0A5B90063C792 /* SampleLayerMask.cpp */; };
 		009CC9190F65918A002185BE /* SampleFontScalerTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 009CC9180F65918A002185BE /* SampleFontScalerTest.cpp */; };
-		00A41E4B0EFC312F00C9CBEB /* SampleArc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A41E4A0EFC312F00C9CBEB /* SampleArc.cpp */; };
 		00A728270FD43D0400D5051F /* SampleMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6760FCCCB01002BD8B4 /* SampleMovie.cpp */; };
 		00A7282F0FD43D3700D5051F /* SkMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A7282D0FD43D3700D5051F /* SkMovie.cpp */; };
 		00A7295D0FD8397600D5051F /* SampleAll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6740FCCCB01002BD8B4 /* SampleAll.cpp */; };
@@ -147,8 +145,6 @@
 		00003C7F0EFC22E1000FF73A /* SkOSWindow_Mac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkOSWindow_Mac.cpp; path = ../../src/utils/mac/SkOSWindow_Mac.cpp; sourceTree = SOURCE_ROOT; };
 		00003C8C0EFC230E000FF73A /* effects.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = effects.xcodeproj; path = ../effects/effects.xcodeproj; sourceTree = SOURCE_ROOT; };
 		00003C9A0EFC233F000FF73A /* SkDOM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkDOM.cpp; path = ../../src/xml/SkDOM.cpp; sourceTree = SOURCE_ROOT; };
-		00003C9B0EFC233F000FF73A /* SkParse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkParse.cpp; path = ../../src/xml/SkParse.cpp; sourceTree = SOURCE_ROOT; };
-		00003C9C0EFC233F000FF73A /* SkParseColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkParseColor.cpp; path = ../../src/xml/SkParseColor.cpp; sourceTree = SOURCE_ROOT; };
 		00003C9D0EFC233F000FF73A /* SkXMLParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkXMLParser.cpp; path = ../../src/xml/SkXMLParser.cpp; sourceTree = SOURCE_ROOT; };
 		00003CA30EFC235F000FF73A /* SkXMLParser_empty.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkXMLParser_empty.cpp; path = ../../src/ports/SkXMLParser_empty.cpp; sourceTree = SOURCE_ROOT; };
 		002884490EFAA35C0083E387 /* core.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = core.xcodeproj; path = ../core/core.xcodeproj; sourceTree = SOURCE_ROOT; };
@@ -303,8 +299,6 @@
 			children = (
 				00003CA30EFC235F000FF73A /* SkXMLParser_empty.cpp */,
 				00003C9A0EFC233F000FF73A /* SkDOM.cpp */,
-				00003C9B0EFC233F000FF73A /* SkParse.cpp */,
-				00003C9C0EFC233F000FF73A /* SkParseColor.cpp */,
 				00003C9D0EFC233F000FF73A /* SkXMLParser.cpp */,
 				00003C7E0EFC22E1000FF73A /* skia_mac.cp */,
 				00003C7F0EFC22E1000FF73A /* SkOSWindow_Mac.cpp */,
@@ -524,11 +518,8 @@
 				00003C810EFC22E1000FF73A /* skia_mac.cp in Sources */,
 				00003C820EFC22E1000FF73A /* SkOSWindow_Mac.cpp in Sources */,
 				00003C9E0EFC233F000FF73A /* SkDOM.cpp in Sources */,
-				00003C9F0EFC233F000FF73A /* SkParse.cpp in Sources */,
-				00003CA00EFC233F000FF73A /* SkParseColor.cpp in Sources */,
 				00003CA10EFC233F000FF73A /* SkXMLParser.cpp in Sources */,
 				00003CA40EFC235F000FF73A /* SkXMLParser_empty.cpp in Sources */,
-				00A41E4B0EFC312F00C9CBEB /* SampleArc.cpp in Sources */,
 				0041CDDB0F00975E00695E8C /* SampleImageDir.cpp in Sources */,
 				0041CDF30F009ED100695E8C /* SkImageRef.cpp in Sources */,
 				0041CDF60F009EED00695E8C /* SkImageRef_GlobalPool.cpp in Sources */,
@@ -579,6 +570,7 @@
 				00A7282F0FD43D3700D5051F /* SkMovie.cpp in Sources */,
 				00A7295D0FD8397600D5051F /* SampleAll.cpp in Sources */,
 				00A729650FD93ED600D5051F /* SampleTestGL.cpp in Sources */,
+				000A99820FD97526007E45BD /* SampleArc.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/xcode/sampleapp_sdl/SDLApp.xcodeproj/project.pbxproj b/xcode/sampleapp_sdl/SDLApp.xcodeproj/project.pbxproj
index 2e28322..b3a38f1 100644
--- a/xcode/sampleapp_sdl/SDLApp.xcodeproj/project.pbxproj
+++ b/xcode/sampleapp_sdl/SDLApp.xcodeproj/project.pbxproj
@@ -55,8 +55,6 @@
 		0064EE980FC7318500D71FB0 /* SkImageRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0064EE950FC7318500D71FB0 /* SkImageRef.cpp */; };
 		0064EEB30FC7333300D71FB0 /* SkXMLParser_empty.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0064EEB20FC7333300D71FB0 /* SkXMLParser_empty.cpp */; };
 		0064EEC50FC7336100D71FB0 /* SkDOM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0064EEB70FC7336100D71FB0 /* SkDOM.cpp */; };
-		0064EEC60FC7336100D71FB0 /* SkParse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0064EEB80FC7336100D71FB0 /* SkParse.cpp */; };
-		0064EEC70FC7336100D71FB0 /* SkParseColor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0064EEB90FC7336100D71FB0 /* SkParseColor.cpp */; };
 		0064EEC80FC7336100D71FB0 /* SkXMLParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0064EEBA0FC7336100D71FB0 /* SkXMLParser.cpp */; };
 		0064EECA0FC7336100D71FB0 /* SkOSWindow_Mac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0064EEBC0FC7336100D71FB0 /* SkOSWindow_Mac.cpp */; };
 		0064EECB0FC7336100D71FB0 /* SkEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0064EEBD0FC7336100D71FB0 /* SkEvent.cpp */; };
@@ -198,8 +196,6 @@
 		0064EE950FC7318500D71FB0 /* SkImageRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkImageRef.cpp; path = ../../src/images/SkImageRef.cpp; sourceTree = SOURCE_ROOT; };
 		0064EEB20FC7333300D71FB0 /* SkXMLParser_empty.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkXMLParser_empty.cpp; path = ../../src/ports/SkXMLParser_empty.cpp; sourceTree = SOURCE_ROOT; };
 		0064EEB70FC7336100D71FB0 /* SkDOM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkDOM.cpp; path = ../../src/xml/SkDOM.cpp; sourceTree = SOURCE_ROOT; };
-		0064EEB80FC7336100D71FB0 /* SkParse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkParse.cpp; path = ../../src/xml/SkParse.cpp; sourceTree = SOURCE_ROOT; };
-		0064EEB90FC7336100D71FB0 /* SkParseColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkParseColor.cpp; path = ../../src/xml/SkParseColor.cpp; sourceTree = SOURCE_ROOT; };
 		0064EEBA0FC7336100D71FB0 /* SkXMLParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkXMLParser.cpp; path = ../../src/xml/SkXMLParser.cpp; sourceTree = SOURCE_ROOT; };
 		0064EEBC0FC7336100D71FB0 /* SkOSWindow_Mac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkOSWindow_Mac.cpp; path = ../../src/utils/mac/SkOSWindow_Mac.cpp; sourceTree = SOURCE_ROOT; };
 		0064EEBD0FC7336100D71FB0 /* SkEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkEvent.cpp; path = ../../src/views/SkEvent.cpp; sourceTree = SOURCE_ROOT; };
@@ -316,8 +312,6 @@
 			isa = PBXGroup;
 			children = (
 				0064EEB70FC7336100D71FB0 /* SkDOM.cpp */,
-				0064EEB80FC7336100D71FB0 /* SkParse.cpp */,
-				0064EEB90FC7336100D71FB0 /* SkParseColor.cpp */,
 				0064EEBA0FC7336100D71FB0 /* SkXMLParser.cpp */,
 				0064EEBC0FC7336100D71FB0 /* SkOSWindow_Mac.cpp */,
 				0064EEBD0FC7336100D71FB0 /* SkEvent.cpp */,
@@ -607,8 +601,6 @@
 				0064EE980FC7318500D71FB0 /* SkImageRef.cpp in Sources */,
 				0064EEB30FC7333300D71FB0 /* SkXMLParser_empty.cpp in Sources */,
 				0064EEC50FC7336100D71FB0 /* SkDOM.cpp in Sources */,
-				0064EEC60FC7336100D71FB0 /* SkParse.cpp in Sources */,
-				0064EEC70FC7336100D71FB0 /* SkParseColor.cpp in Sources */,
 				0064EEC80FC7336100D71FB0 /* SkXMLParser.cpp in Sources */,
 				0064EECA0FC7336100D71FB0 /* SkOSWindow_Mac.cpp in Sources */,
 				0064EECB0FC7336100D71FB0 /* SkEvent.cpp in Sources */,