Update filter tool to write out paths to .cpp file

https://codereview.appspot.com/6843125/



git-svn-id: http://skia.googlecode.com/svn/trunk@6714 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/tools.gyp b/gyp/tools.gyp
index 8a31eaa..0cdb319 100644
--- a/gyp/tools.gyp
+++ b/gyp/tools.gyp
@@ -199,6 +199,8 @@
       ],
       'sources': [
         '../tools/filtermain.cpp',
+        '../tools/path_utils.cpp',
+        '../tools/path_utils.h',
       ],
       'dependencies': [
         'skia_base_libs.gyp:skia_base_libs',
diff --git a/tools/filtermain.cpp b/tools/filtermain.cpp
index 5dbdc22..28020cd 100644
--- a/tools/filtermain.cpp
+++ b/tools/filtermain.cpp
@@ -15,14 +15,16 @@
 #include "SkPictureRecord.h"
 #include "SkStream.h"
 #include "picture_utils.h"
+#include "path_utils.h"
 
 static void usage() {
-    SkDebugf("Usage: filter -i inFile [-o outFile] [-t textureDir] [-h|--help]");
-    SkDebugf("\n\n");
+    SkDebugf("Usage: filter -i inFile [-o outFile] [--input-dir path] [--output-dir path]\n");
+    SkDebugf("                        [-p pathFile] [-t textureDir] [-h|--help]\n\n");
     SkDebugf("    -i inFile  : file to file.\n");
     SkDebugf("    -o outFile : result of filtering.\n");
     SkDebugf("    --input-dir : process all files in dir with .skp extension.\n");
     SkDebugf("    --output-dir : results of filtering the input dir.\n");
+    SkDebugf("    -p pathFile : file in which to place compileable path data.\n");
     SkDebugf("    -t textureDir : directory in which to place textures. (only available w/ single file)\n");
     SkDebugf("    -h|--help  : Show this help message.\n");
 }
@@ -30,12 +32,30 @@
 // SkFilterRecord allows the filter to manipulate the read in SkPicture
 class SkFilterRecord : public SkPictureRecord {
 public:
-    SkFilterRecord(uint32_t recordFlags, SkDevice* device)
+    SkFilterRecord(uint32_t recordFlags, SkDevice* device, SkFILEWStream* pathStream)
         : INHERITED(recordFlags, device)
         , fTransSkipped(0)
         , fTransTot(0)
         , fScalesSkipped(0)
-        , fScalesTot(0) {
+        , fScalesTot(0)
+        , fPathStream(pathStream) {
+    }
+
+    virtual ~SkFilterRecord() {
+    }
+
+    virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAntiAlias) SK_OVERRIDE {
+        if (!path.isRect(NULL) && 4 < path.countPoints()) {
+            sk_tools::dump_path(fPathStream, path);
+        }
+        return INHERITED::clipPath(path, op, doAntiAlias);
+    }
+
+    virtual void drawPath(const SkPath& path, const SkPaint& p) SK_OVERRIDE {
+        if (!path.isRect(NULL) && 4 < path.countPoints()) {
+            sk_tools::dump_path(fPathStream, path);
+        }
+        INHERITED::drawPath(path, p);
     }
 
     virtual bool translate(SkScalar dx, SkScalar dy) SK_OVERRIDE {
@@ -97,6 +117,7 @@
     int fScalesSkipped;
     int fScalesTot;
 
+    SkFILEWStream* fPathStream;
 private:
     typedef SkPictureRecord INHERITED;
 };
@@ -119,7 +140,8 @@
     return SkImageEncoder::EncodeStream(stream, bitmap, SkImageEncoder::kPNG_Type, 100);
 }
 
-int filter_picture(const SkString& inFile, const SkString& outFile, const SkString& textureDir) {
+int filter_picture(const SkString& inFile, const SkString& outFile, 
+                   const SkString& textureDir, SkFILEWStream *pathStream) {
     SkPicture* inPicture = NULL;
 
     SkFILEStream inStream(inFile.c_str());
@@ -136,7 +158,7 @@
     bm.setConfig(SkBitmap::kNo_Config, inPicture->width(), inPicture->height());
     SkAutoTUnref<SkDevice> dev(SkNEW_ARGS(SkDevice, (bm)));
 
-    SkAutoTUnref<SkFilterRecord> filterRecord(SkNEW_ARGS(SkFilterRecord, (0, dev)));
+    SkAutoTUnref<SkFilterRecord> filterRecord(SkNEW_ARGS(SkFilterRecord, (0, dev, pathStream)));
 
     // Playback the read in picture to the SkFilterRecorder to allow filtering
     filterRecord->beginRecording();
@@ -169,7 +191,7 @@
         return -1;
     }
 
-    SkString inFile, outFile, inDir, outDir, textureDir;
+    SkString inFile, outFile, inDir, outDir, textureDir, pathFile;
 
     char* const* stop = argv + argc;
     for (++argv; argv < stop; ++argv) {
@@ -209,6 +231,15 @@
                 usage();
                 return -1;
             }
+        } else if (strcmp(*argv, "-p") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                pathFile.set(*argv);
+            } else {
+                SkDebugf("missing arg for -p\n");
+                usage();
+                return -1;
+            }
         } else if (strcmp(*argv, "-t") == 0) {
             argv++;
             if (argv < stop && **argv) {
@@ -234,6 +265,19 @@
         return -1;
     }
 
+    SkFILEWStream *pathStream = NULL;
+
+    if (!pathFile.isEmpty()) {
+        pathStream = new SkFILEWStream(pathFile.c_str());
+        if (!pathStream->isValid()) {
+            SkDebugf("Could open path file %s\n", pathFile.c_str());
+            delete pathStream;
+            return -1;
+        }
+
+        sk_tools::dump_path_prefix(pathStream);
+    }
+
     SkOSFile::Iter iter(inDir.c_str(), "skp");
     int failures = 0;
     SkString inputFilename, outputFilename;
@@ -245,16 +289,26 @@
                 sk_tools::make_filepath(&outFile, outDir, inputFilename);
             }
             SkDebugf("Executing %s\n", inputFilename.c_str());
-            filter_picture(inFile, outFile, textureDir);
+            filter_picture(inFile, outFile, textureDir, pathStream);
         } while(iter.next(&inputFilename));
 
     } else if (!inFile.isEmpty()) {
-        filter_picture(inFile, outFile, textureDir);
+        filter_picture(inFile, outFile, textureDir, pathStream);
     } else {
         usage();
+        if (NULL != pathStream) {
+            delete pathStream;
+            pathStream = NULL;
+        }
         return -1;
     }
 
+    if (NULL != pathStream) {
+        sk_tools::dump_path_suffix(pathStream);
+        delete pathStream;
+        pathStream = NULL;
+    }
+
     SkGraphics::Term();
     return 0;
 }
diff --git a/tools/path_utils.cpp b/tools/path_utils.cpp
new file mode 100644
index 0000000..217554d
--- /dev/null
+++ b/tools/path_utils.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "path_utils.h"
+#include "SkPath.h"
+#include "SkStream.h"
+
+namespace sk_tools {
+    static int gCurPathID = 0;
+
+    void dump_path_prefix(SkFILEWStream* pathStream) {
+        if (NULL == pathStream) {
+            return;
+        }
+ 
+        pathStream->writeText("#include \"SkScalar.h\"\n");
+        pathStream->writeText("#include \"SkPoint.h\"\n");
+        pathStream->writeText("#include \"SkBitmap.h\"\n");
+        pathStream->writeText("#include \"SkDevice.h\"\n");
+        pathStream->writeText("#include \"SkString.h\"\n");
+        pathStream->writeText("#include \"SkImageEncoder.h\"\n");
+    }
+
+    void dump_path(SkFILEWStream* pathStream, const SkPath& path) {
+        if (NULL == pathStream) {
+            return;
+        }
+
+        static const int kMaxPts = 200;
+        static const int kMaxVerbs = 200;
+
+        int numPts = path.countPoints();
+        int numVerbs = path.countVerbs();
+
+        SkASSERT(numPts <= kMaxPts);
+        SkASSERT(numVerbs <= kMaxVerbs);
+
+        SkPoint pts[kMaxPts];
+        uint8_t verbs[kMaxVerbs];
+
+        path.getPoints(pts, kMaxPts);
+        path.getVerbs(verbs, kMaxVerbs);
+
+        const char* gStrs[] = {
+            "kMove_Verb",
+            "kLine_Verb",
+            "kQuad_Verb",
+            "kCubic_Verb",
+            "kClose_Verb",
+            "kDone_Verb"
+        };
+
+        pathStream->writeText("static const int numPts");
+        pathStream->writeDecAsText(gCurPathID);
+        pathStream->writeText(" = ");
+        pathStream->writeDecAsText(numPts);
+        pathStream->writeText(";\n");
+
+        pathStream->writeText("SkPoint pts");
+        pathStream->writeDecAsText(gCurPathID);
+        pathStream->writeText("[] = {\n");
+
+        for (int i = 0; i < numPts; ++i) {
+            SkString temp;
+
+            pathStream->writeText("      { ");
+            temp.appendScalar(pts[i].fX);
+            temp.append("f, ");
+            temp.appendScalar(pts[i].fY);
+            temp.append("f },\n");
+            pathStream->writeText(temp.c_str());
+        }
+        pathStream->writeText("};\n");
+
+        pathStream->writeText("static const int numVerbs");
+        pathStream->writeDecAsText(gCurPathID);
+        pathStream->writeText(" = ");
+        pathStream->writeDecAsText(numVerbs);
+        pathStream->writeText(";\n");
+            
+        pathStream->writeText("uint8_t verbs");
+        pathStream->writeDecAsText(gCurPathID);
+        pathStream->writeText("[] = {\n");
+
+        for (int i = 0; i < numVerbs; ++i) {
+            pathStream->writeText("\tSkPath::");
+            pathStream->writeText(gStrs[verbs[i]]);
+            pathStream->writeText(",\n");
+        }
+        pathStream->writeText("};\n");
+
+        gCurPathID++;
+    }
+
+    void dump_path_suffix(SkFILEWStream* pathStream) {
+        if (NULL == pathStream) {
+            return;
+        }
+
+        pathStream->writeText("int numPaths = ");
+        pathStream->writeDecAsText(gCurPathID);
+        pathStream->writeText(";\n");
+
+        pathStream->writeText("int sizes[] = {\n");
+        for (int i = 0; i < gCurPathID; ++i) {
+            pathStream->writeText("\t numPts");
+            pathStream->writeDecAsText(i);
+            pathStream->writeText(", numVerbs");
+            pathStream->writeDecAsText(i);
+            pathStream->writeText(",\n");
+        }
+        pathStream->writeText("};\n");
+
+        pathStream->writeText("const SkPoint* points[] = {\n");
+        for (int i = 0; i < gCurPathID; ++i) {
+            pathStream->writeText("\tpts");
+            pathStream->writeDecAsText(i);
+            pathStream->writeText(",\n");
+        }
+        pathStream->writeText("};\n");
+
+        pathStream->writeText("const uint8_t* verbs[] = {\n");
+        for (int i = 0; i < gCurPathID; ++i) {
+            pathStream->writeText("\t(const uint8_t*)verbs");
+            pathStream->writeDecAsText(i);
+            pathStream->writeText(",\n");
+        }
+        pathStream->writeText("};\n");
+    }
+}
diff --git a/tools/path_utils.h b/tools/path_utils.h
new file mode 100644
index 0000000..d459611
--- /dev/null
+++ b/tools/path_utils.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef path_utils_DEFINED
+#define path_utils_DEFINED
+
+class SkFILEWStream;
+class SkPath;
+
+namespace sk_tools {
+    // These utilities help write paths to a .cpp file in a compileable form.
+    // To use call them in the order:
+    //      dump_path_prefix - once per program invocation
+    //      dump_path - once for each path of interest
+    //      dump_path_suffix - once per program invocation
+    //
+    // The output system relies on a global current path ID and assumes that
+    // only one set of aggregation arrays will be written per program 
+    // invocation. These utilities are not thread safe.
+
+    // Write of the headers needed to compile the resulting .cpp file
+    void dump_path_prefix(SkFILEWStream* pathStream);
+
+    // Write out a single path in the form:
+    //      static const int numPts# = ...;
+    //      SkPoint pts#[] = { ... };
+    //      static const int numVerbs# = ...;
+    //      uint8_t verbs#[] = { ... };
+    // Where # is a globally unique identifier
+    void dump_path(SkFILEWStream* pathStream, const SkPath& path);
+
+    // Write out structures to aggregate info about the written paths:
+    //      int numPaths = ...;
+    //      int sizes[] = {
+    //          numPts#, numVerbs#,
+    //          ...
+    //      };
+    //      const SkPoint* points[] = { pts#, ... };
+    //      const uint8_t* verbs[] = { verbs#, ... };
+    void dump_path_suffix(SkFILEWStream* pathStream);
+}
+
+#endif
\ No newline at end of file