Add Encoder fuzzers

This also includes a helper "fuzzer" for making a corpus.
Point it at an image or folder of images and it will
decode those images and write the SkPixmap's bytes to
disk, such that the fuzzer will be able to read in
those bytes as if it had decoded the image (or gotten
it from another source).

Bug: skia:
Change-Id: Iaf223a39078f2b62908fb47929add5d63f22d973
Reviewed-on: https://skia-review.googlesource.com/117367
Reviewed-by: Leon Scroggins <scroggo@google.com>
Commit-Queue: Kevin Lubick <kjlubick@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 647bf5b..6b34fcd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1762,6 +1762,7 @@
       "fuzz/FuzzCanvas.cpp",
       "fuzz/FuzzCommon.cpp",
       "fuzz/FuzzDrawFunctions.cpp",
+      "fuzz/FuzzEncoders.cpp",
       "fuzz/FuzzGradients.cpp",
       "fuzz/FuzzParsePath.cpp",
       "fuzz/FuzzPathMeasure.cpp",
diff --git a/fuzz/Fuzz.h b/fuzz/Fuzz.h
index 203a57f..4fb7396 100644
--- a/fuzz/Fuzz.h
+++ b/fuzz/Fuzz.h
@@ -62,6 +62,7 @@
 
     sk_sp<SkData> fBytes;
     size_t fNextByte;
+    friend void fuzz__MakeEncoderCorpus(Fuzz*);
 };
 
 // UBSAN reminds us that bool can only legally hold 0 or 1.
diff --git a/fuzz/FuzzEncoders.cpp b/fuzz/FuzzEncoders.cpp
new file mode 100644
index 0000000..7ca4a48
--- /dev/null
+++ b/fuzz/FuzzEncoders.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Fuzz.h"
+#include "SkBitmap.h"
+#include "SkImage.h"
+#include "SkImageInfo.h"
+#include "SkJpegEncoder.h"
+#include "SkPixmap.h"
+#include "SkPngEncoder.h"
+#include "SkRandom.h"
+#include "SkWebpEncoder.h"
+#include "SkOSFile.h"
+
+#include <vector>
+
+// These values were picked arbitrarily to hopefully limit the size of the
+// serialized SkPixmaps.
+constexpr int MAX_WIDTH = 512;
+constexpr int MAX_HEIGHT = 512;
+
+static SkBitmap make_fuzzed_bitmap(Fuzz* fuzz) {
+    SkBitmap bm;
+    uint32_t w, h;
+    fuzz->nextRange(&w, 1, MAX_WIDTH);
+    fuzz->nextRange(&h, 1, MAX_HEIGHT);
+    if (!bm.tryAllocPixels(SkImageInfo::MakeN32Premul(w, h))) {
+        return bm;
+    }
+    uint32_t n = w * h;
+    fuzz->nextN((SkPMColor*)bm.getPixels(), n);
+    return bm;
+}
+
+DEF_FUZZ(PNGEncoder, fuzz) {
+    auto bm = make_fuzzed_bitmap(fuzz);
+
+    auto opts = SkPngEncoder::Options{};
+    fuzz->nextRange(&opts.fZLibLevel, 0, 9);
+
+    SkDynamicMemoryWStream dest;
+    SkPngEncoder::Encode(&dest, bm.pixmap(), opts);
+}
+
+DEF_FUZZ(JPEGEncoder, fuzz) {
+    auto bm = make_fuzzed_bitmap(fuzz);
+
+    auto opts = SkJpegEncoder::Options{};
+    fuzz->nextRange(&opts.fQuality, 0, 100);
+
+    SkDynamicMemoryWStream dest;
+    (void)SkJpegEncoder::Encode(&dest, bm.pixmap(), opts);
+}
+
+DEF_FUZZ(WEBPEncoder, fuzz) {
+    auto bm = make_fuzzed_bitmap(fuzz);
+
+    auto opts = SkWebpEncoder::Options{};
+    fuzz->nextRange(&opts.fQuality, 0.0f, 100.0f);
+    bool lossy;
+    fuzz->next(&lossy);
+    if (lossy) {
+        opts.fCompression = SkWebpEncoder::Compression::kLossy;
+    } else {
+        opts.fCompression = SkWebpEncoder::Compression::kLossless;
+    }
+
+    SkDynamicMemoryWStream dest;
+    (void)SkWebpEncoder::Encode(&dest, bm.pixmap(), opts);
+}
+
+// Not a real fuzz endpoint, but a helper to take in real, good images
+// and dump out a corpus for this fuzzer.
+DEF_FUZZ(_MakeEncoderCorpus, fuzz) {
+    auto bytes = fuzz->fBytes;
+    SkDebugf("bytes %d\n", bytes->size());
+    auto img = SkImage::MakeFromEncoded(bytes);
+    if (nullptr == img.get()) {
+        SkDebugf("invalid image, could not decode\n");
+        return;
+    }
+    if (img->width() > MAX_WIDTH || img->height() > MAX_HEIGHT) {
+        SkDebugf("Too big (%d x %d)\n", img->width(), img->height());
+        return;
+    }
+    std::vector<int32_t> dstPixels;
+    int rowBytes = img->width() * 4;
+    dstPixels.resize(img->height() * rowBytes);
+    SkPixmap pm(SkImageInfo::MakeN32Premul(img->width(), img->height()),
+        &dstPixels.front(), rowBytes);
+    if (!img->readPixels(pm, 0, 0)) {
+        SkDebugf("Could not read pixmap\n");
+        return;
+    }
+
+    SkString s("./encoded_corpus/enc_");
+    static SkRandom rand;
+    s.appendU32(rand.nextU());
+    auto file = sk_fopen(s.c_str(), SkFILE_Flags::kWrite_SkFILE_Flag);
+    if (!file) {
+        SkDebugf("Can't initialize file\n");
+        return;
+    }
+    auto total = pm.info().bytesPerPixel() * pm.width() * pm.height();
+    SkDebugf("Writing %d (%d x %d) bytes\n", total, pm.width(), pm.height());
+    // Write out the size in two bytes since that's what the fuzzer will
+    // read first.
+    uint32_t w = pm.width();
+    sk_fwrite(&w, sizeof(uint32_t), file);
+    uint32_t h = pm.height();
+    sk_fwrite(&h, sizeof(uint32_t), file);
+    sk_fwrite(pm.addr(), total, file);
+    sk_fclose(file);
+}
diff --git a/fuzz/oss_fuzz/FuzzJPEGEncoder.cpp b/fuzz/oss_fuzz/FuzzJPEGEncoder.cpp
new file mode 100644
index 0000000..3d5ce41
--- /dev/null
+++ b/fuzz/oss_fuzz/FuzzJPEGEncoder.cpp
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2018 Google, LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "../Fuzz.h"
+
+void fuzz_JPEGEncoder(Fuzz* f);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    auto fuzz = Fuzz(SkData::MakeWithoutCopy(data, size));
+    fuzz_JPEGEncoder(&fuzz);
+    return 0;
+}
diff --git a/fuzz/oss_fuzz/FuzzPNGEncoder.cpp b/fuzz/oss_fuzz/FuzzPNGEncoder.cpp
new file mode 100644
index 0000000..8e10416
--- /dev/null
+++ b/fuzz/oss_fuzz/FuzzPNGEncoder.cpp
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2018 Google, LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "../Fuzz.h"
+
+void fuzz_PNGEncoder(Fuzz* f);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    auto fuzz = Fuzz(SkData::MakeWithoutCopy(data, size));
+    fuzz_PNGEncoder(&fuzz);
+    return 0;
+}
diff --git a/fuzz/oss_fuzz/FuzzWEBPEncoder.cpp b/fuzz/oss_fuzz/FuzzWEBPEncoder.cpp
new file mode 100644
index 0000000..f1da38d
--- /dev/null
+++ b/fuzz/oss_fuzz/FuzzWEBPEncoder.cpp
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2018 Google, LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "../Fuzz.h"
+
+void fuzz_WEBPEncoder(Fuzz* f);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    auto fuzz = Fuzz(SkData::MakeWithoutCopy(data, size));
+    fuzz_WEBPEncoder(&fuzz);
+    return 0;
+}