Add SkPngChunkReader.

This class allows a client of SkCodec to read chunks in the data
stream that are not recognized by libpng. This is used by Android
to specify ninepatch data.

Taken from SkImageDecoder::Peeker. Modify the name of the class
and its method to be more specific to their use. Make
SkImageDecoder::Peeker a subclass of the new class, to help stage
the change in Android.

Add a test to verify that it works.

BUG=skia:4574
BUG=skia:3257

Committed: https://skia.googlesource.com/skia/+/3389e00136188800b98ca69488c0418c374fd78b

Review URL: https://codereview.chromium.org/1040453002
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 071a4b8..11eb1f9 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -25,7 +25,6 @@
 };
 
 static const DecoderProc gDecoderProcs[] = {
-    { SkPngCodec::IsPng, SkPngCodec::NewFromStream },
 #if !defined(GOOGLE3)
     { SkJpegCodec::IsJpeg, SkJpegCodec::NewFromStream },
 #endif
@@ -36,7 +35,8 @@
     { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream }
 };
 
-SkCodec* SkCodec::NewFromStream(SkStream* stream) {
+SkCodec* SkCodec::NewFromStream(SkStream* stream,
+                                SkPngChunkReader* chunkReader) {
     if (!stream) {
         return nullptr;
     }
@@ -44,15 +44,24 @@
     SkAutoTDelete<SkStream> streamDeleter(stream);
     
     SkAutoTDelete<SkCodec> codec(nullptr);
-    for (uint32_t i = 0; i < SK_ARRAY_COUNT(gDecoderProcs); i++) {
-        DecoderProc proc = gDecoderProcs[i];
-        const bool correctFormat = proc.IsFormat(stream);
-        if (!stream->rewind()) {
-            return nullptr;
-        }
-        if (correctFormat) {
-            codec.reset(proc.NewFromStream(streamDeleter.detach()));
-            break;
+    // PNG is special, since we want to be able to supply an SkPngChunkReader.
+    // But this code follows the same pattern as the loop.
+    const bool isPng = SkPngCodec::IsPng(stream);
+    if (!stream->rewind()) {
+        return NULL;
+    }
+    if (isPng) {
+        codec.reset(SkPngCodec::NewFromStream(streamDeleter.detach(), chunkReader));
+    } else {
+        for (DecoderProc proc : gDecoderProcs) {
+            const bool correctFormat = proc.IsFormat(stream);
+            if (!stream->rewind()) {
+                return nullptr;
+            }
+            if (correctFormat) {
+                codec.reset(proc.NewFromStream(streamDeleter.detach()));
+                break;
+            }
         }
     }
 
@@ -68,11 +77,11 @@
     }
 }
 
-SkCodec* SkCodec::NewFromData(SkData* data) {
+SkCodec* SkCodec::NewFromData(SkData* data, SkPngChunkReader* reader) {
     if (!data) {
         return nullptr;
     }
-    return NewFromStream(new SkMemoryStream(data));
+    return NewFromStream(new SkMemoryStream(data), reader);
 }
 
 SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream)
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
index 82c952e..355d493 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -65,6 +65,14 @@
     }
 }
 
+#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
+    SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(png_ptr);
+    // readChunk() returning true means continue decoding
+    return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk->size) ? 1 : -1;
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 // Helpers
 ///////////////////////////////////////////////////////////////////////////////
@@ -211,14 +219,29 @@
     return true;
 }
 
-// Reads the header, and initializes the passed in fields, if not nullptr (except
-// stream, which is passed to the read function).
-// Returns true on success, in which case the caller is responsible for calling
-// png_destroy_read_struct. If it returns false, the passed in fields (except
-// stream) are unchanged.
-static bool read_header(SkStream* stream, png_structp* png_ptrp,
-                        png_infop* info_ptrp, SkImageInfo* imageInfo,
-                        int* bitDepthPtr, int* numberPassesPtr) {
+// Reads the header and initializes the output fields, if not NULL.
+//
+// @param stream Input data. Will be read to get enough information to properly
+//      setup the codec.
+// @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL.
+//      If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is
+//      expected to continue to own it for the lifetime of the png_ptr.
+// @param png_ptrp Optional output variable. If non-NULL, will be set to a new
+//      png_structp on success.
+// @param info_ptrp Optional output variable. If non-NULL, will be set to a new
+//      png_infop on success;
+// @param imageInfo Optional output variable. If non-NULL, will be set to
+//      reflect the properties of the encoded image on success.
+// @param bitDepthPtr Optional output variable. If non-NULL, will be set to the
+//      bit depth of the encoded image on success.
+// @param numberPassesPtr Optional output variable. If non-NULL, will be set to
+//      the number_passes of the encoded image on success.
+// @return true on success, in which case the caller is responsible for calling
+//      png_destroy_read_struct(png_ptrp, info_ptrp).
+//      If it returns false, the passed in fields (except stream) are unchanged.
+static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader,
+                        png_structp* png_ptrp, png_infop* info_ptrp,
+                        SkImageInfo* imageInfo, int* bitDepthPtr, int* numberPassesPtr) {
     // The image is known to be a PNG. Decode enough to know the SkImageInfo.
     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
                                                  sk_error_fn, sk_warning_fn);
@@ -243,10 +266,14 @@
 
     png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn);
 
-    // FIXME: This is where the old code hooks up the Peeker. Does it need to
-    // be set this early? (i.e. where are the user chunks? early in the stream,
-    // potentially?)
-    // If it does, we need to figure out a way to set it here.
+#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+    // FIXME: Does this need to be installed so early?
+    // hookup our chunkReader so we can see any user-chunks the caller may be interested in
+    if (chunkReader) {
+        png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
+        png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk);
+    }
+#endif
 
     // The call to png_read_info() gives us all of the information from the
     // PNG file before the first IDAT (image data chunk).
@@ -356,9 +383,10 @@
     return true;
 }
 
-SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
+SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, SkPngChunkReader* chunkReader,
                        png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses)
     : INHERITED(info, stream)
+    , fPngChunkReader(SkSafeRef(chunkReader))
     , fPng_ptr(png_ptr)
     , fInfo_ptr(info_ptr)
     , fSrcConfig(SkSwizzler::kUnknown)
@@ -453,7 +481,8 @@
 
     png_structp png_ptr;
     png_infop info_ptr;
-    if (!read_header(this->stream(), &png_ptr, &info_ptr, nullptr, nullptr, nullptr)) {
+    if (!read_header(this->stream(), fPngChunkReader.get(), &png_ptr, &info_ptr,
+                     nullptr, nullptr, nullptr)) {
         return false;
     }
 
@@ -602,8 +631,8 @@
 class SkPngScanlineDecoder : public SkPngCodec {
 public:
     SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
-            png_structp png_ptr, png_infop info_ptr, int bitDepth)
-        : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, 1)
+            SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_ptr, int bitDepth)
+        : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1)
         , fAlphaState(kUnknown_AlphaState)
         , fSrcRow(nullptr)
     {}
@@ -686,8 +715,9 @@
 class SkPngInterlacedScanlineDecoder : public SkPngCodec {
 public:
     SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
-            png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses)
-        : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, numberPasses)
+            SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_ptr,
+            int bitDepth, int numberPasses)
+        : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, numberPasses)
         , fAlphaState(kUnknown_AlphaState)
         , fHeight(-1)
         , fCanSkipRewind(false)
@@ -822,7 +852,7 @@
     typedef SkPngCodec INHERITED;
 };
 
-SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
+SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) {
     SkAutoTDelete<SkStream> streamDeleter(stream);
     png_structp png_ptr;
     png_infop info_ptr;
@@ -830,15 +860,16 @@
     int bitDepth;
     int numberPasses;
 
-    if (!read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth, &numberPasses)) {
+    if (!read_header(stream, chunkReader, &png_ptr, &info_ptr, &imageInfo, &bitDepth,
+                     &numberPasses)) {
         return nullptr;
     }
 
     if (1 == numberPasses) {
-        return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr, info_ptr,
-                                        bitDepth);
+        return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), chunkReader,
+                                        png_ptr, info_ptr, bitDepth);
     }
 
-    return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr,
-                                              info_ptr, bitDepth, numberPasses);
+    return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(), chunkReader,
+                                              png_ptr, info_ptr, bitDepth, numberPasses);
 }
diff --git a/src/codec/SkCodec_libpng.h b/src/codec/SkCodec_libpng.h
index 9809b0c..c2a5f4a 100644
--- a/src/codec/SkCodec_libpng.h
+++ b/src/codec/SkCodec_libpng.h
@@ -7,6 +7,7 @@
 
 #include "SkCodec.h"
 #include "SkColorTable.h"
+#include "SkPngChunkReader.h"
 #include "SkEncodedFormat.h"
 #include "SkImageInfo.h"
 #include "SkRefCnt.h"
@@ -21,7 +22,7 @@
     static bool IsPng(SkStream*);
 
     // Assume IsPng was called and returned true.
-    static SkCodec* NewFromStream(SkStream*);
+    static SkCodec* NewFromStream(SkStream*, SkPngChunkReader* = NULL);
 
     virtual ~SkPngCodec();
 
@@ -41,7 +42,7 @@
         return fSwizzler;
     }
 
-    SkPngCodec(const SkImageInfo&, SkStream*, png_structp, png_infop, int, int);
+    SkPngCodec(const SkImageInfo&, SkStream*, SkPngChunkReader*, png_structp, png_infop, int, int);
 
     png_structp png_ptr() { return fPng_ptr; }
     SkSwizzler* swizzler() { return fSwizzler; }
@@ -62,17 +63,18 @@
     virtual AlphaState alphaInScanlineDecode() const = 0;
 
 private:
-    png_structp                 fPng_ptr;
-    png_infop                   fInfo_ptr;
+    SkAutoTUnref<SkPngChunkReader>  fPngChunkReader;
+    png_structp                     fPng_ptr;
+    png_infop                       fInfo_ptr;
 
     // These are stored here so they can be used both by normal decoding and scanline decoding.
-    SkAutoTUnref<SkColorTable>  fColorTable;    // May be unpremul.
-    SkAutoTDelete<SkSwizzler>   fSwizzler;
+    SkAutoTUnref<SkColorTable>      fColorTable;    // May be unpremul.
+    SkAutoTDelete<SkSwizzler>       fSwizzler;
 
-    SkSwizzler::SrcConfig       fSrcConfig;
-    const int                   fNumberPasses;
-    int                         fBitDepth;
-    AlphaState                  fAlphaState;
+    SkSwizzler::SrcConfig           fSrcConfig;
+    const int                       fNumberPasses;
+    int                             fBitDepth;
+    AlphaState                      fAlphaState;
 
     bool decodePalette(bool premultiply, int* ctableCount);
     void destroyReadStruct();