Spin off non-core parts of ok into their own files.

Now ok.cpp handles only the high level coordination of Srcs and Dsts,
without having to know or care what they are.

Some minor refactoring to things like Options.

Change-Id: I02df890b26d6d069e980a125b6a1ce1a7067b900
Reviewed-on: https://skia-review.googlesource.com/10173
Reviewed-by: Mike Klein <mtklein@chromium.org>
Commit-Queue: Mike Klein <mtklein@chromium.org>
diff --git a/tools/ok.cpp b/tools/ok.cpp
index db82b2f..bfef28f 100644
--- a/tools/ok.cpp
+++ b/tools/ok.cpp
@@ -11,17 +11,11 @@
 
 #include "SkGraphics.h"
 #include "SkOSFile.h"
-#include "SkPicture.h"
-#include "SkSurface.h"
-#include "gm.h"
+#include "ok.h"
 #include <chrono>
-#include <functional>
 #include <future>
 #include <list>
-#include <map>
-#include <memory>
 #include <regex>
-#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <thread>
@@ -161,178 +155,17 @@
     };
 #endif
 
-struct Src {
-    virtual ~Src() {}
-    virtual std::string name() = 0;
-    virtual SkISize     size() = 0;
-    virtual void draw(SkCanvas*) = 0;
-};
-
-struct Stream {
-    virtual ~Stream() {}
-    virtual std::unique_ptr<Src> next() = 0;
-};
-
-struct Options {
-    std::map<std::string, std::string> kv;
-
-    explicit Options(std::string str = "") {
-        std::string k,v, *curr = &k;
-        for (auto c : str) {
-            switch(c) {
-                case ',': kv[k] = v;
-                          curr = &(k = "");
-                          break;
-                case '=': curr = &(v = "");
-                          break;
-                default: *curr += c;
-            }
-        }
-        kv[k] = v;
-    }
-
-    std::string lookup(std::string k, std::string fallback = "") {
-        for (auto it = kv.find(k); it != kv.end(); ) {
-            return it->second;
-        }
-        return fallback;
-    }
-};
-
-template <typename T>
-static std::unique_ptr<T> move_unique(T& v) {
-    return std::unique_ptr<T>{new T{std::move(v)}};
-}
-
-struct GMStream : Stream {
-    const skiagm::GMRegistry* registry = skiagm::GMRegistry::Head();
-
-    static std::unique_ptr<Stream> Create(Options) {
-        GMStream stream;
-        return move_unique(stream);
-    }
-
-    struct GMSrc : Src {
-        skiagm::GM* (*factory)(void*);
-        std::unique_ptr<skiagm::GM> gm;
-
-        std::string name() override {
-            gm.reset(factory(nullptr));
-            return gm->getName();
-        }
-
-        SkISize size() override {
-            return gm->getISize();
-        }
-
-        void draw(SkCanvas* canvas) override {
-            canvas->clear(0xffffffff);
-            canvas->concat(gm->getInitialTransform());
-            gm->draw(canvas);
-        }
-    };
-
-    std::unique_ptr<Src> next() override {
-        if (!registry) {
-            return nullptr;
-        }
-        GMSrc src;
-        src.factory = registry->factory();
-        registry = registry->next();
-        return move_unique(src);
-    }
-};
-
-struct SKPStream : Stream {
-    std::string dir;
-    std::vector<std::string> skps;
-
-    static std::unique_ptr<Stream> Create(Options options) {
-        SKPStream stream;
-        stream.dir = options.lookup("dir", "skps");
-        SkOSFile::Iter it{stream.dir.c_str(), ".skp"};
-        for (SkString path; it.next(&path); ) {
-            stream.skps.push_back(path.c_str());
-        }
-        return move_unique(stream);
-    }
-
-    struct SKPSrc : Src {
-        std::string dir, path;
-        sk_sp<SkPicture> pic;
-
-        std::string name() override {
-            return path;
-        }
-
-        SkISize size() override {
-            auto skp = SkData::MakeFromFileName((dir+"/"+path).c_str());
-            pic = SkPicture::MakeFromData(skp.get());
-            return pic->cullRect().roundOut().size();
-        }
-
-        void draw(SkCanvas* canvas) override {
-            canvas->clear(0xffffffff);
-            pic->playback(canvas);
-        }
-    };
-
-    std::unique_ptr<Src> next() override {
-        if (skps.empty()) {
-            return nullptr;
-        }
-        SKPSrc src;
-        src.dir  = dir;
-        src.path = skps.back();
-        skps.pop_back();
-        return move_unique(src);
-    }
-};
-
-struct Dst {
-    virtual ~Dst() {}
-    virtual SkCanvas* canvas() = 0;
-    virtual void write(std::string path_prefix) = 0;
-};
-
-struct SWDst : Dst {
-    sk_sp<SkSurface> surface;
-
-    static std::unique_ptr<Dst> Create(SkISize size, Options options) {
-        SkImageInfo info = SkImageInfo::MakeN32Premul(size.width(), size.height());
-        if (options.lookup("ct") == "565") { info = info.makeColorType(kRGB_565_SkColorType); }
-        if (options.lookup("ct") == "f16") { info = info.makeColorType(kRGBA_F16_SkColorType); }
-        SWDst dst;
-        dst.surface = SkSurface::MakeRaster(info);
-        return move_unique(dst);
-    }
-
-    SkCanvas* canvas() override {
-        return surface->getCanvas();
-    }
-
-    void write(std::string path_prefix) override {
-        auto image = surface->makeImageSnapshot();
-        sk_sp<SkData> png{image->encode()};
-        SkFILEWStream{(path_prefix + ".png").c_str()}.write(png->data(), png->size());
-    }
-};
-
-struct {
+struct StreamType {
     const char* name;
     std::unique_ptr<Stream> (*factory)(Options);
-} streams[] = {
-    {"gm",  GMStream::Create },
-    {"skp", SKPStream::Create },
 };
+static std::vector<StreamType> stream_types;
 
-struct {
+struct DstType {
     const char* name;
     std::unique_ptr<Dst> (*factory)(SkISize, Options);
-} dsts[] = {
-    {"sw",  SWDst::Create },
 };
-
+static std::vector<DstType> dst_types;
 
 int main(int argc, char** argv) {
     SkGraphics::Init();
@@ -343,23 +176,22 @@
     std::regex  search      {".*"};
     std::string write_dir   {""};
 
-    std::unique_ptr<Stream> stream;
-    std::unique_ptr<Dst> (*dst_factory)(SkISize, Options) = nullptr;
-    Options dst_options;
+    std::unique_ptr<Stream>                       stream;
+    std::function<std::unique_ptr<Dst> (SkISize)> dst_factory;
 
     auto help = [&] {
-        std::string stream_types, dst_types;
-        for (auto s : streams) {
-            if (!stream_types.empty()) {
-                stream_types += ", ";
+        std::string stream_names, dst_names;
+        for (auto s : stream_types) {
+            if (!stream_names.empty()) {
+                stream_names += ", ";
             }
-            stream_types += s.name;
+            stream_names += s.name;
         }
-        for (auto d : dsts) {
-            if (!dst_types.empty()) {
-                dst_types += ", ";
+        for (auto d : dst_types) {
+            if (!dst_names.empty()) {
+                dst_names += ", ";
             }
-            dst_types += d.name;
+            dst_names += d.name;
         }
 
         printf("%s [-j N] [-m regex] [-s regex] [-w dir] [-h] src[:k=v,...] dst[:k=v,...] \n"
@@ -374,7 +206,7 @@
                 " src: content to draw: %s                                                \n"
                 " dst: how to draw that content: %s                                       \n"
                 " Some srcs and dsts have options, e.g. skp:dir=skps sw:ct=565            \n",
-                argv[0], stream_types.c_str(), dst_types.c_str());
+                argv[0], stream_names.c_str(), dst_names.c_str());
         return 1;
     };
 
@@ -385,7 +217,7 @@
         if (0 == strcmp("-w", argv[i])) { write_dir =      argv[++i] ; }
         if (0 == strcmp("-h", argv[i])) { return help(); }
 
-        for (auto s : streams) {
+        for (auto s : stream_types) {
             size_t len = strlen(s.name);
             if (0 == strncmp(s.name, argv[i], len)) {
                 switch (argv[i][len]) {
@@ -394,13 +226,14 @@
                 }
             }
         }
-        for (auto d : dsts) {
+        for (auto d : dst_types) {
             size_t len = strlen(d.name);
             if (0 == strncmp(d.name, argv[i], len)) {
                 switch (argv[i][len]) {
                     case  ':': len++;
-                    case '\0': dst_factory = d.factory;
-                               dst_options = Options{argv[i]+len};
+                    case '\0': dst_factory = [=](SkISize size){
+                                   return d.factory(size, Options{argv[i]+len});
+                               };
                 }
             }
         }
@@ -463,7 +296,7 @@
                 return Status::Skipped;
             }
 
-            auto dst = dst_factory(src->size(), dst_options);
+            auto dst = dst_factory(src->size());
 
             auto canvas = dst->canvas();
             src->draw(canvas);
@@ -483,3 +316,33 @@
     printf("\n");
     return (failed || crashed) ? 1 : 0;
 }
+
+
+Register::Register(const char* name, std::unique_ptr<Stream> (*factory)(Options)) {
+    stream_types.push_back(StreamType{name, factory});
+}
+Register::Register(const char* name, std::unique_ptr<Dst> (*factory)(SkISize, Options)) {
+    dst_types.push_back(DstType{name, factory});
+}
+
+Options::Options(std::string str) {
+    std::string k,v, *curr = &k;
+    for (auto c : str) {
+        switch(c) {
+            case ',': this->kv[k] = v;
+                      curr = &(k = "");
+                      break;
+            case '=': curr = &(v = "");
+                      break;
+            default: *curr += c;
+        }
+    }
+    this->kv[k] = v;
+}
+
+std::string Options::operator()(std::string k, std::string fallback) const {
+    for (auto it = kv.find(k); it != kv.end(); ) {
+        return it->second;
+    }
+    return fallback;
+}