DM: support non-fatal errors

Tasks that produce a non-fatal error will bail out before writing their output to
disk and hash to dm.json, but not count as failures.

This also makes true failures bail out before writing their results.  If the DM
program failed, we probably don't want to triage that image result.

We use this new feature first to skip image subset decoding when we detect it's
not supported.  Here's a snippet of an example run, where in this case only
.webp are subset decodable:

...
(  15MB    12) 172µs	8888 subset color_wheel.jpg (skipped: Subset decoding not supported.)
(  15MB    11) 9.05ms	8888 subset randPixels.webp
(  16MB    10) 863µs	8888 subset baby_tux.png (skipped: Subset decoding not supported.)
...

Only outputs corresponding to the .webp show up, both on disk and in the .json.

BUG=skia:

Review URL: https://codereview.chromium.org/980333002
diff --git a/dm/DM.cpp b/dm/DM.cpp
index f9c2ff7..4ab5da9 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -186,7 +186,7 @@
     SkDynamicMemoryWStream stream;
     SkString log;
     Error err = sink->draw(noop, &bitmap, &stream, &log);
-    if (!err.isEmpty()) {
+    if (err.isFatal()) {
         SkDebugf("Skipping %s: %s\n", tag, err.c_str());
         return;
     }
@@ -326,11 +326,18 @@
             SkDynamicMemoryWStream stream;
             Error err = task->sink->draw(*task->src, &bitmap, &stream, &log);
             if (!err.isEmpty()) {
-                fail(SkStringPrintf("%s %s %s: %s",
-                                    task->sink.tag,
-                                    task->src.tag,
-                                    name.c_str(),
-                                    err.c_str()));
+                timer.end();
+                if (err.isFatal()) {
+                    fail(SkStringPrintf("%s %s %s: %s",
+                                        task->sink.tag,
+                                        task->src.tag,
+                                        name.c_str(),
+                                        err.c_str()));
+                } else {
+                    name.appendf(" (skipped: %s)", err.c_str());
+                }
+                done(timer.fWall, task->sink.tag, task->src.tag, name, log);
+                return;
             }
             SkAutoTDelete<SkStreamAsset> data(stream.detachAsStream());
 
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 2060ce2..d99f011 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -92,7 +92,7 @@
     stream->rewind();
     int w,h;
     if (!decoder->buildTileIndex(stream.detach(), &w, &h) || w*h == 1) {
-        return "";  // Not an error.  Subset decoding is not always supported.
+        return Error::Nonfatal("Subset decoding not supported.");
     }
 
     // Divide the image into subsets that cover the entire image.
diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h
index faa3333..dcd169e 100644
--- a/dm/DMSrcSink.h
+++ b/dm/DMSrcSink.h
@@ -18,10 +18,33 @@
     template <typename T>
     ImplicitString(const T& s) : SkString(s) {}
 };
-typedef ImplicitString Error;
 typedef ImplicitString Name;
 typedef ImplicitString Path;
 
+class Error {
+public:
+    Error(const SkString& s) : fMsg(s), fFatal(!this->isEmpty()) {}
+    Error(const char* s)     : fMsg(s), fFatal(!this->isEmpty()) {}
+
+    Error(const Error&)            = default;
+    Error& operator=(const Error&) = default;
+
+    static Error Nonfatal(const SkString& s) { return Nonfatal(s.c_str()); }
+    static Error Nonfatal(const char* s) {
+        Error e(s);
+        e.fFatal = false;
+        return e;
+    }
+
+    const char* c_str() const { return fMsg.c_str(); }
+    bool isEmpty() const { return fMsg.isEmpty(); }
+    bool isFatal() const { return fFatal; }
+
+private:
+    SkString fMsg;
+    bool     fFatal;
+};
+
 struct Src {
     // All Srcs must be thread safe.
     virtual ~Src() {}