Add SkCodec, including PNG implementation.
authorscroggo <scroggo@google.com>
Mon, 2 Mar 2015 20:23:48 +0000 (12:23 -0800)
committerCommit bot <commit-bot@chromium.org>
Mon, 2 Mar 2015 20:23:48 +0000 (12:23 -0800)
DM:
Add a flag to use SkCodec instead of SkImageDecoder.

SkCodec:
Base class for codecs, allowing creation from an SkStream or an SkData.
An SkCodec, on creation, knows properties of the data like its width and height. Further calls can be used to generate the image.
TODO: Add scanline iterator

SkPngCodec:
New decoder for png. Wraps libpng. The code has been repurposed from SkImageDecoder_libpng.
TODO: Handle other destination colortypes
TODO: Substitute the transpose color
TODO: Allow silencing warnings
TODO: Use RGB instead of filler?
TODO: sRGB

SkSwizzler:
Simplified version of SkScaledSampler. Unlike the sampler, this object does no sampling.
TODO: Implement other swizzles.

BUG=skia:3257

Review URL: https://codereview.chromium.org/930283002

13 files changed:
DEPS
dm/DMSrcSink.cpp
gyp/codec.gyp [new file with mode: 0644]
gyp/common_variables.gypi
gyp/copy_file.py [new file with mode: 0644]
gyp/libpng.gyp
gyp/skia_lib.gyp
include/codec/SkCodec.h [new file with mode: 0644]
src/codec/SkCodec.cpp [new file with mode: 0644]
src/codec/SkCodec_libpng.cpp [new file with mode: 0644]
src/codec/SkCodec_libpng.h [new file with mode: 0644]
src/codec/SkSwizzler.cpp [new file with mode: 0644]
src/codec/SkSwizzler.h [new file with mode: 0644]

diff --git a/DEPS b/DEPS
index 3c16bd9..20e0e9a 100644 (file)
--- a/DEPS
+++ b/DEPS
@@ -9,15 +9,16 @@ deps = {
   #   - both Android and ChromeOS pull the same giflib;
   #   - can use use our existing t_p/e/libjpeg instead of pulling it for Android?
 
-  "third_party/externals/angle2" : "https://chromium.googlesource.com/angle/angle.git@c415283b2bcd786e1a8c55c19ef3511eb2b3928c",
-  "third_party/externals/freetype" : "https://skia.googlesource.com/third_party/freetype2.git@VER-2-5-0-1",
-  "third_party/externals/gyp" : "https://chromium.googlesource.com/external/gyp.git@dd831fd86e7a254c696f53944333562466e453ad",
+  "third_party/externals/angle2"  : "https://chromium.googlesource.com/angle/angle.git@c415283b2bcd786e1a8c55c19ef3511eb2b3928c",
+  "third_party/externals/freetype": "https://skia.googlesource.com/third_party/freetype2.git@VER-2-5-0-1",
+  "third_party/externals/gyp"     : "https://chromium.googlesource.com/external/gyp.git@dd831fd86e7a254c696f53944333562466e453ad",
   "third_party/externals/harfbuzz": "https://skia.googlesource.com/third_party/harfbuzz.git@0.9.35",
   "third_party/externals/jsoncpp" : "https://chromium.googlesource.com/external/jsoncpp/jsoncpp.git@1afff032c83e26ddf7f2776e8b43de5ad666c1fa",
   "third_party/externals/libjpeg" : "https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@82ce8a6d4ebe12a177c0c3597192f2b4f09e81c3",
   "third_party/externals/libwebp" : "https://chromium.googlesource.com/webm/libwebp.git@3fe91635df8734b23f3c1b9d1f0c4fa8cfaf4e39",
   "third_party/externals/nanomsg" : "https://skia.googlesource.com/third_party/nanomsg.git@0.4-beta",
   "third_party/externals/zlib"    : "https://chromium.googlesource.com/chromium/src/third_party/zlib@4ba7cdd0e7bf49d671645264f839838fc56e1492",
+  "third_party/externals/libpng"  : "git://git.code.sf.net/p/libpng/code@070a616b8275277e18ef8ee91e2ca23f7bdc67d5",
 
   "platform_tools/android/third_party/externals/expat" : "https://android.googlesource.com/platform/external/expat.git@android-4.2.2_r1.2",
   "platform_tools/android/third_party/externals/gif" : "https://android.googlesource.com/platform/external/giflib.git@android-4.2.2_r1.2",
index e67d435..67f8ac6 100644 (file)
@@ -1,6 +1,7 @@
 #include "DMSrcSink.h"
 #include "SamplePipeControllers.h"
 #include "SkCommonFlags.h"
+#include "SkCodec.h"
 #include "SkDocument.h"
 #include "SkError.h"
 #include "SkMultiPictureDraw.h"
@@ -12,6 +13,8 @@
 #include "SkStream.h"
 #include "SkXMLWriter.h"
 
+DEFINE_bool(codec, false, "Use SkCodec instead of SkImageDecoder");
+
 namespace DM {
 
 GMSrc::GMSrc(skiagm::GMRegistry::Factory factory) : fFactory(factory) {}
@@ -46,9 +49,35 @@ Error ImageSrc::draw(SkCanvas* canvas) const {
     if (fDivisor == 0) {
         // Decode the full image.
         SkBitmap bitmap;
-        if (!SkImageDecoder::DecodeMemory(encoded->data(), encoded->size(), &bitmap,
-                                          dstColorType, SkImageDecoder::kDecodePixels_Mode)) {
-            return SkStringPrintf("Couldn't decode %s.", fPath.c_str());
+        if (FLAGS_codec) {
+            SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
+            if (!codec) {
+                return SkStringPrintf("Couldn't decode %s.", fPath.c_str());
+            }
+            SkImageInfo info;
+            if (!codec->getInfo(&info)) {
+                return SkStringPrintf("Couldn't getInfo %s.", fPath.c_str());
+            }
+            info = info.makeColorType(dstColorType);
+            if (info.alphaType() == kUnpremul_SkAlphaType) {
+                // FIXME: Currently we cannot draw unpremultiplied sources.
+                info = info.makeAlphaType(kPremul_SkAlphaType);
+            }
+            if (!bitmap.tryAllocPixels(info)) {
+                return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(),
+                                      info.width(), info.height());
+            }
+            SkAutoLockPixels alp(bitmap);
+            const SkImageGenerator::Result result = codec->getPixels(info, bitmap.getPixels(),
+                                                                     bitmap.rowBytes());
+            if (result != SkImageGenerator::kSuccess) {
+                return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str());
+            }
+        } else {
+            if (!SkImageDecoder::DecodeMemory(encoded->data(), encoded->size(), &bitmap,
+                                              dstColorType, SkImageDecoder::kDecodePixels_Mode)) {
+                return SkStringPrintf("Couldn't decode %s.", fPath.c_str());
+            }
         }
         encoded.reset((SkData*)NULL);  // Might as well drop this when we're done with it.
         canvas->drawBitmap(bitmap, 0,0);
diff --git a/gyp/codec.gyp b/gyp/codec.gyp
new file mode 100644 (file)
index 0000000..b962799
--- /dev/null
@@ -0,0 +1,29 @@
+# GYP file for codec project.
+{
+  'targets': [
+    {
+      'target_name': 'codec',
+      'product_name': 'skia_codec',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'dependencies': [
+        'core.gyp:*',
+        'libpng.gyp:libpng',
+      ],
+      'include_dirs': [
+        '../include/codec',
+        '../src/codec',
+      ],
+      'sources': [
+        '../src/codec/SkCodec.cpp',
+        '../src/codec/SkCodec_libpng.cpp',
+        '../src/codec/SkSwizzler.cpp',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../include/codec',
+        ],
+      },
+    },
+  ],
+}
index 9750187..dd2c084 100644 (file)
             'skia_freetype_static%': '0',
           }
         ],
+        [ 'skia_os in ["mac", "ios", "win"]', {
+            # skia_libpng_static - instead of linking libpng with '-lpng' and
+            #     including the headers from '/usr/include/png.h', compile and
+            #     statically link the version of libpng in
+            #     third_party/externals/libpng.
+            'skia_libpng_static%': '1',
+          }, {
+            'skia_libpng_static%': '0',
+          }
+        ],
       ],
 
       # skia_giflib_static - on OS variants that normally would link giflib
       #     giflib in third_party/externals/giflib.
       'skia_giflib_static%': '0',
 
-      # skia_libpng_static - on OS variants that normally would link libpng
-      #     with '-lpng' and include the headers from '/usr/include/png.h',
-      #     don't do that; instead compile and staticlly link the version of
-      #     libpng in third_party/externals/libpng.
-      'skia_libpng_static%': '0',
 
       # skia_no_fontconfig - On POSIX systems that would normally use the
       #     SkFontHost_fontconfig interface; use the SkFontHost_linux
diff --git a/gyp/copy_file.py b/gyp/copy_file.py
new file mode 100644 (file)
index 0000000..7268bf2
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Copy a file.
+"""
+
+import argparse
+import os
+import shutil
+
+if __name__ == '__main__':
+  parser = argparse.ArgumentParser()
+  parser.add_argument('src', help='File to copy.')
+  parser.add_argument('dst', help='Location to copy to.')
+  args = parser.parse_args()
+
+  src = os.path.abspath(os.path.join(os.getcwd(), args.src))
+  dst = os.path.abspath(os.path.join(os.getcwd(), args.dst))
+
+  print 'Copying from %s to %s' % (src, dst)
+
+  src_dir = os.path.dirname(src)
+  if not os.path.exists(src_dir):
+    raise AssertionError('src directory %s does not exist!' % src_dir)
+
+  if not os.path.exists(src):
+    raise AssertionError('file to copy %s does not exist' % src)
+
+  dst_dir = os.path.dirname(dst)
+  if not os.path.exists(dst_dir):
+    print 'dst directory %s does not exist! creating it!' % dst_dir
+    os.makedirs(dst_dir)
+
+  shutil.copyfile(src, dst)
index 92ff8d5..96120a8 100644 (file)
               '-w',
               '-fvisibility=hidden',
             ],
+            'conditions': [
+              ['not arm_neon', {
+                'defines': [
+                    # FIXME: Why is this needed? Without it, pngpriv.h sets it
+                    # to 2 if __ARM_NEON is defined, but shouldn't __ARM_NEON
+                    # not be defined since arm_neon is 0?
+                    'PNG_ARM_NEON_OPT=0',
+                ],
+              }],
+            ],
+            'actions': [
+              {
+                'action_name': 'generate_pngconf',
+                'variables' : {
+                  'prebuilt': '../third_party/externals/libpng/scripts/pnglibconf.h.prebuilt',
+                  'generated': '../third_party/externals/libpng/pnglibconf.h',
+                },
+                'inputs': [
+                  '<(prebuilt)',
+                ],
+                'outputs': [
+                  '<(generated)',
+                ],
+                'action': ['python', 'copy_file.py', '<(prebuilt)', '<(generated)'],
+              },
+            ],
             'sources': [
               '../third_party/externals/libpng/png.c',
               '../third_party/externals/libpng/pngerror.c',
-              '../third_party/externals/libpng/pnggccrd.c',
               '../third_party/externals/libpng/pngget.c',
               '../third_party/externals/libpng/pngmem.c',
               '../third_party/externals/libpng/pngpread.c',
@@ -45,7 +70,6 @@
               '../third_party/externals/libpng/pngrutil.c',
               '../third_party/externals/libpng/pngset.c',
               '../third_party/externals/libpng/pngtrans.c',
-              '../third_party/externals/libpng/pngvcrd.c',
               '../third_party/externals/libpng/pngwio.c',
               '../third_party/externals/libpng/pngwrite.c',
               '../third_party/externals/libpng/pngwtran.c',
index cc4984a..80d4f8f 100644 (file)
@@ -4,6 +4,7 @@
   'variables': {
     'component_libs': [
       'core.gyp:core',
+      'codec.gyp:codec',
       'effects.gyp:effects',
       'images.gyp:images',
       'opts.gyp:opts',
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
new file mode 100644 (file)
index 0000000..beb9cb9
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCodec_DEFINED
+#define SkCodec_DEFINED
+
+#include "SkImageGenerator.h"
+#include "SkImageInfo.h"
+#include "SkSize.h"
+#include "SkTemplates.h"
+#include "SkTypes.h"
+
+class SkData;
+class SkStream;
+
+/**
+ *  Abstraction layer directly on top of an image codec.
+ */
+class SkCodec : public SkImageGenerator {
+public:
+    /**
+     *  If this stream represents an encoded image that we know how to decode,
+     *  return an SkCodec that can decode it. Otherwise return NULL.
+     *
+     *  If NULL is returned, the stream is deleted immediately. Otherwise, the
+     *  SkCodec takes ownership of it, and will delete it when done with it.
+     */
+    static SkCodec* NewFromStream(SkStream*);
+
+    /**
+     *  If this data represents an encoded image that we know how to decode,
+     *  return an SkCodec that can decode it. Otherwise return NULL.
+     *
+     *  Will take a ref if it returns a codec, else will not affect the data.
+     */
+    static SkCodec* NewFromData(SkData*);
+
+    /**
+     *  Return a size that approximately supports the desired scale factor.
+     *  The codec may not be able to scale efficiently to the exact scale
+     *  factor requested, so return a size that approximates that scale.
+     *
+     *  FIXME: Move to SkImageGenerator?
+     */
+    SkISize getScaledDimensions(float desiredScale) const;
+
+protected:
+    SkCodec(const SkImageInfo&, SkStream*);
+
+    /**
+     *  The SkAlphaType is a conservative answer. i.e. it is possible that it
+     *  initially returns a non-opaque answer, but completing the decode
+     *  reveals that the image is actually opaque.
+     */
+    bool onGetInfo(SkImageInfo* info) SK_OVERRIDE {
+        *info = fInfo;
+        return true;
+    }
+
+    // Helper for subclasses.
+    const SkImageInfo& getOriginalInfo() { return fInfo; }
+
+    virtual SkISize onGetScaledDimensions(float /* desiredScale */) const {
+        // By default, scaling is not supported.
+        return fInfo.dimensions();
+    }
+
+    /**
+     *  If the stream was previously read, attempt to rewind.
+     *  @returns:
+     *      true
+     *       - if the stream needed to be rewound, and the rewind
+     *         succeeded.
+     *       - if the stream did not need to be rewound.
+     *      false
+     *       - if the stream needed to be rewound, and rewind failed.
+     *  Subclasses MUST call this function before reading the stream (e.g. in
+     *  onGetPixels). If it returns false, onGetPixels should return
+     *  kCouldNotRewind.
+     */
+    bool SK_WARN_UNUSED_RESULT rewindIfNeeded();
+
+private:
+    const SkImageInfo fInfo;
+    SkAutoTDelete<SkStream> fStream;
+    bool fNeedsRewind;
+};
+#endif // SkCodec_DEFINED
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
new file mode 100644 (file)
index 0000000..ec36bc7
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec.h"
+#include "SkData.h"
+#include "SkCodec_libpng.h"
+#include "SkStream.h"
+
+SkCodec* SkCodec::NewFromStream(SkStream* stream) {
+    if (!stream) {
+        return NULL;
+    }
+    SkAutoTDelete<SkStream> streamDeleter(stream);
+    const bool isPng = SkPngCodec::IsPng(stream);
+    // TODO: Avoid rewinding.
+    if (!stream->rewind()) {
+        return NULL;
+    }
+    if (isPng) {
+        streamDeleter.detach();
+        return SkPngCodec::NewFromStream(stream);
+    }
+    // TODO: Check other image types.
+    return NULL;
+}
+
+SkCodec* SkCodec::NewFromData(SkData* data) {
+    if (!data) {
+        return NULL;
+    }
+    return NewFromStream(SkNEW_ARGS(SkMemoryStream, (data)));
+}
+
+SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream)
+    : fInfo(info)
+    , fStream(stream)
+    , fNeedsRewind(false)
+{}
+
+bool SkCodec::rewindIfNeeded() {
+    // Store the value of fNeedsRewind so we can update it. Next read will
+    // require a rewind.
+    const bool neededRewind = fNeedsRewind;
+    fNeedsRewind = true;
+    return !neededRewind || fStream->rewind();
+}
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
new file mode 100644 (file)
index 0000000..8e7ee33
--- /dev/null
@@ -0,0 +1,492 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec_libpng.h"
+#include "SkColorPriv.h"
+#include "SkColorTable.h"
+#include "SkBitmap.h"
+#include "SkMath.h"
+#include "SkSize.h"
+#include "SkStream.h"
+#include "SkSwizzler.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Helper macros
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef png_jmpbuf
+#  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
+#endif
+
+/* These were dropped in libpng >= 1.4 */
+#ifndef png_infopp_NULL
+    #define png_infopp_NULL NULL
+#endif
+
+#ifndef png_bytepp_NULL
+    #define png_bytepp_NULL NULL
+#endif
+
+#ifndef int_p_NULL
+    #define int_p_NULL NULL
+#endif
+
+#ifndef png_flush_ptr_NULL
+    #define png_flush_ptr_NULL NULL
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Callback functions
+///////////////////////////////////////////////////////////////////////////////
+
+static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
+    SkDebugf("------ png error %s\n", msg);
+    longjmp(png_jmpbuf(png_ptr), 1);
+}
+
+static void sk_read_fn(png_structp png_ptr, png_bytep data,
+                       png_size_t length) {
+    SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr));
+    const size_t bytes = stream->read(data, length);
+    if (bytes != length) {
+        // FIXME: We want to report the fact that the stream was truncated.
+        // One way to do that might be to pass a enum to longjmp so setjmp can
+        // specify the failure.
+        png_error(png_ptr, "Read Error!");
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers
+///////////////////////////////////////////////////////////////////////////////
+
+class AutoCleanPng : public SkNoncopyable {
+public:
+    AutoCleanPng(png_structp png_ptr)
+        : fPng_ptr(png_ptr)
+        , fInfo_ptr(NULL) {}
+
+    ~AutoCleanPng() {
+        // fInfo_ptr will never be non-NULL unless fPng_ptr is.
+        if (fPng_ptr) {
+            png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : NULL;
+            png_destroy_read_struct(&fPng_ptr, info_pp, png_infopp_NULL);
+        }
+    }
+
+    void setInfoPtr(png_infop info_ptr) {
+        SkASSERT(NULL == fInfo_ptr);
+        fInfo_ptr = info_ptr;
+    }
+
+    void detach() {
+        fPng_ptr = NULL;
+        fInfo_ptr = NULL;
+    }
+
+private:
+    png_structp     fPng_ptr;
+    png_infop       fInfo_ptr;
+};
+#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
+
+// call only if color_type is PALETTE. Returns true if the ctable has alpha
+static bool has_transparency_in_palette(png_structp png_ptr,
+                                        png_infop info_ptr) {
+    if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+        return false;
+    }
+
+    png_bytep trans;
+    int num_trans;
+    png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
+    return num_trans > 0;
+}
+
+// Method for coverting to either an SkPMColor or a similarly packed
+// unpremultiplied color.
+typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
+
+// Note: SkColorTable claims to store SkPMColors, which is not necessarily
+// the case here.
+SkColorTable* decode_palette(png_structp png_ptr, png_infop info_ptr,
+                             bool premultiply, SkAlphaType* outAlphaType) {
+    SkASSERT(outAlphaType != NULL);
+    int numPalette;
+    png_colorp palette;
+    png_bytep trans;
+
+    if (!png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette)) {
+        return NULL;
+    }
+
+    /*  BUGGY IMAGE WORKAROUND
+
+        We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
+        which is a problem since we use the byte as an index. To work around this we grow
+        the colortable by 1 (if its < 256) and duplicate the last color into that slot.
+    */
+    const int colorCount = numPalette + (numPalette < 256);
+    // Note: These are not necessarily SkPMColors.
+    SkPMColor colorStorage[256];    // worst-case storage
+    SkPMColor* colorPtr = colorStorage;
+
+    int numTrans;
+    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+        png_get_tRNS(png_ptr, info_ptr, &trans, &numTrans, NULL);
+    } else {
+        numTrans = 0;
+    }
+
+    // check for bad images that might make us crash
+    if (numTrans > numPalette) {
+        numTrans = numPalette;
+    }
+
+    int index = 0;
+    int transLessThanFF = 0;
+
+    // Choose which function to use to create the color table. If the final destination's
+    // colortype is unpremultiplied, the color table will store unpremultiplied colors.
+    PackColorProc proc;
+    if (premultiply) {
+        proc = &SkPreMultiplyARGB;
+    } else {
+        proc = &SkPackARGB32NoCheck;
+    }
+    for (; index < numTrans; index++) {
+        transLessThanFF |= (int)*trans - 0xFF;
+        *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
+        palette++;
+    }
+
+    if (transLessThanFF < 0) {
+        *outAlphaType = premultiply ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
+    } else {
+        *outAlphaType = kOpaque_SkAlphaType;
+    }
+
+    for (; index < numPalette; index++) {
+        *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
+        palette++;
+    }
+
+    // see BUGGY IMAGE WORKAROUND comment above
+    if (numPalette < 256) {
+        *colorPtr = colorPtr[-1];
+    }
+
+    return SkNEW_ARGS(SkColorTable, (colorStorage, colorCount));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Creation
+///////////////////////////////////////////////////////////////////////////////
+
+#define PNG_BYTES_TO_CHECK 4
+
+bool SkPngCodec::IsPng(SkStream* stream) {
+    char buf[PNG_BYTES_TO_CHECK];
+    if (stream->read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK) {
+        return false;
+    }
+    if (png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
+        return false;
+    }
+    return true;
+}
+
+SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
+    // The image is known to be a PNG. Decode enough to know the SkImageInfo.
+    // FIXME: Allow silencing warnings.
+    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
+                                                 sk_error_fn, NULL);
+    if (!png_ptr) {
+        return NULL;
+    }
+
+    AutoCleanPng autoClean(png_ptr);
+
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (info_ptr == NULL) {
+        return NULL;
+    }
+
+    autoClean.setInfoPtr(info_ptr);
+
+    // FIXME: Could we use the return value of setjmp to specify the type of
+    // error?
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        return NULL;
+    }
+
+    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.
+
+    // The call to png_read_info() gives us all of the information from the
+    // PNG file before the first IDAT (image data chunk).
+    png_read_info(png_ptr, info_ptr);
+    png_uint_32 origWidth, origHeight;
+    int bitDepth, colorType;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
+                 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
+
+    // sanity check for size
+    {
+        int64_t size = sk_64_mul(origWidth, origHeight);
+        // now check that if we are 4-bytes per pixel, we also don't overflow
+        if (size < 0 || size > (0x7FFFFFFF >> 2)) {
+            return NULL;
+        }
+    }
+
+    // Tell libpng to strip 16 bit/color files down to 8 bits/color
+    if (bitDepth == 16) {
+        png_set_strip_16(png_ptr);
+    }
+#ifdef PNG_READ_PACK_SUPPORTED
+    // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
+    // byte into separate bytes (useful for paletted and grayscale images).
+    if (bitDepth < 8) {
+        png_set_packing(png_ptr);
+    }
+#endif
+    // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
+    if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
+        png_set_expand_gray_1_2_4_to_8(png_ptr);
+    }
+
+
+    // Now determine the default SkColorType and SkAlphaType.
+    SkColorType skColorType;
+    SkAlphaType skAlphaType;
+    switch (colorType) {
+        case PNG_COLOR_TYPE_PALETTE:
+            // Technically, this is true of the data, but I don't think we want
+            // to support it.
+            // skColorType = kIndex8_SkColorType;
+            skColorType = kN32_SkColorType;
+            skAlphaType = has_transparency_in_palette(png_ptr, info_ptr) ?
+                    kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
+            break;
+        case PNG_COLOR_TYPE_GRAY:
+            if (false) {
+                // FIXME: Is this the wrong default behavior? This means if the
+                // caller supplies the info we gave them, they'll get Alpha 8.
+                skColorType = kAlpha_8_SkColorType;
+                // FIXME: Strangely, the canonical type for Alpha 8 is Premul.
+                skAlphaType = kPremul_SkAlphaType;
+            } else {
+                skColorType = kN32_SkColorType;
+                skAlphaType = kOpaque_SkAlphaType;
+            }
+            break;
+        default:
+            // Note: This *almost* mimics the code in SkImageDecoder_libpng.
+            // has_transparency_in_palette makes an additional check - whether
+            // numTrans is greater than 0. Why does the other code not make that
+            // check?
+            if (has_transparency_in_palette(png_ptr, info_ptr)
+                || PNG_COLOR_TYPE_RGB_ALPHA == colorType
+                || PNG_COLOR_TYPE_GRAY_ALPHA == colorType)
+            {
+                skAlphaType = kUnpremul_SkAlphaType;
+            } else {
+                skAlphaType = kOpaque_SkAlphaType;
+            }
+            skColorType = kN32_SkColorType;
+            break;
+    }
+
+    {
+        // FIXME: Again, this block needs to go into onGetPixels.
+        bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType && skColorType != kAlpha_8_SkColorType;
+
+        // Unless the user is requesting A8, convert a grayscale image into RGB.
+        // GRAY_ALPHA will always be converted to RGB
+        if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+            png_set_gray_to_rgb(png_ptr);
+        }
+
+        // Add filler (or alpha) byte (after each RGB triplet) if necessary.
+        // FIXME: It seems like we could just use RGB as the SrcConfig here.
+        if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) {
+            png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+        }
+    }
+
+    // FIXME: Also need to check for sRGB (skbug.com/3471).
+
+    SkImageInfo info = SkImageInfo::Make(origWidth, origHeight, skColorType,
+                                         skAlphaType);
+    SkCodec* codec = SkNEW_ARGS(SkPngCodec, (info, stream, png_ptr, info_ptr));
+    autoClean.detach();
+    return codec;
+}
+
+SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
+                       png_structp png_ptr, png_infop info_ptr)
+    : INHERITED(info, stream)
+    , fPng_ptr(png_ptr)
+    , fInfo_ptr(info_ptr) {}
+
+SkPngCodec::~SkPngCodec() {
+    png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Getting the pixels
+///////////////////////////////////////////////////////////////////////////////
+
+static bool premul_and_unpremul(SkAlphaType A, SkAlphaType B) {
+    return kPremul_SkAlphaType == A && kUnpremul_SkAlphaType == B;
+}
+
+static bool conversion_possible(const SkImageInfo& A, const SkImageInfo& B) {
+    // TODO: Support other conversions
+    if (A.colorType() != B.colorType()) {
+        return false;
+    }
+    if (A.profileType() != B.profileType()) {
+        return false;
+    }
+    if (A.alphaType() == B.alphaType()) {
+        return true;
+    }
+    return premul_and_unpremul(A.alphaType(), B.alphaType())
+            || premul_and_unpremul(B.alphaType(), A.alphaType());
+}
+
+SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
+                                        size_t rowBytes, SkPMColor ctable[],
+                                        int* ctableCount) {
+    if (!this->rewindIfNeeded()) {
+        return kCouldNotRewind;
+    }
+    if (requestedInfo.dimensions() != this->getOriginalInfo().dimensions()) {
+        return kInvalidScale;
+    }
+    if (!conversion_possible(requestedInfo, this->getOriginalInfo())) {
+        return kInvalidConversion;
+    }
+
+    SkBitmap decodedBitmap;
+    // If installPixels would have failed, getPixels should have failed before
+    // calling onGetPixels.
+    SkAssertResult(decodedBitmap.installPixels(requestedInfo, dst, rowBytes));
+
+    // Initialize all non-trivial objects before setjmp.
+    SkAutoTUnref<SkColorTable> colorTable;
+    SkAutoTDelete<SkSwizzler> swizzler;
+    SkAutoMalloc storage;                   // Scratch memory for pre-swizzled rows.
+
+    // FIXME: Could we use the return value of setjmp to specify the type of
+    // error?
+    if (setjmp(png_jmpbuf(fPng_ptr))) {
+        SkDebugf("setjmp long jump!\n");
+        return kInvalidInput;
+    }
+
+    // FIXME: We already retrieved this information. Store it in SkPngCodec?
+    png_uint_32 origWidth, origHeight;
+    int bitDepth, pngColorType, interlaceType;
+    png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth,
+                 &pngColorType, &interlaceType, int_p_NULL, int_p_NULL);
+
+    const int numberPasses = (interlaceType != PNG_INTERLACE_NONE) ?
+            png_set_interlace_handling(fPng_ptr) : 1;
+
+    SkSwizzler::SrcConfig sc;
+    bool reallyHasAlpha = false;
+    if (PNG_COLOR_TYPE_PALETTE == pngColorType) {
+        sc = SkSwizzler::kIndex;
+        SkAlphaType at = requestedInfo.alphaType();
+        colorTable.reset(decode_palette(fPng_ptr, fInfo_ptr,
+                                        kPremul_SkAlphaType == at,
+                                        &at));
+        if (!colorTable) {
+            return kInvalidInput;
+        }
+
+        reallyHasAlpha = (at != kOpaque_SkAlphaType);
+
+        if (at != requestedInfo.alphaType()) {
+            // It turns out the image is opaque.
+            SkASSERT(kOpaque_SkAlphaType == at);
+        }
+    } else if (kAlpha_8_SkColorType == requestedInfo.colorType()) {
+        // Note: we check the destination, since otherwise we would have
+        // told png to upscale.
+        SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
+        sc = SkSwizzler::kGray;
+    } else if (this->getOriginalInfo().alphaType() == kOpaque_SkAlphaType) {
+        sc = SkSwizzler::kRGBX;
+    } else {
+        sc = SkSwizzler::kRGBA;
+    }
+    const SkPMColor* colors = colorTable ? colorTable->readColors() : NULL;
+    // TODO: Support skipZeroes.
+    swizzler.reset(SkSwizzler::CreateSwizzler(sc, colors, requestedInfo,
+                                              dst, rowBytes, false));
+    if (!swizzler) {
+        // FIXME: CreateSwizzler could fail for another reason.
+        return kUnimplemented;
+    }
+
+    // FIXME: Here is where we should likely insert some of the modifications
+    // made in the factory.
+    png_read_update_info(fPng_ptr, fInfo_ptr);
+
+    if (numberPasses > 1) {
+        const int width = requestedInfo.width();
+        const int height = requestedInfo.height();
+        const int bpp = SkSwizzler::BytesPerPixel(sc);
+        const size_t rowBytes = width * bpp;
+
+        storage.reset(width * height * bpp);
+        uint8_t* const base = static_cast<uint8_t*>(storage.get());
+
+        for (int i = 0; i < numberPasses; i++) {
+            uint8_t* row = base;
+            for (int y = 0; y < height; y++) {
+                uint8_t* bmRow = row;
+                png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
+                row += rowBytes;
+            }
+        }
+
+        // Now swizzle it.
+        uint8_t* row = base;
+        for (int y = 0; y < height; y++) {
+            reallyHasAlpha |= swizzler->next(row);
+            row += rowBytes;
+        }
+    } else {
+        storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(sc));
+        uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
+        for (int y = 0; y < requestedInfo.height(); y++) {
+            png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
+            reallyHasAlpha |= swizzler->next(srcRow);
+        }
+    }
+
+    /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
+    png_read_end(fPng_ptr, fInfo_ptr);
+
+    // FIXME: do we need substituteTranspColor?
+
+    if (reallyHasAlpha && requestedInfo.alphaType() != kOpaque_SkAlphaType) {
+        // FIXME: We want to alert the caller. Is this the right way?
+        SkImageInfo* modInfo = const_cast<SkImageInfo*>(&requestedInfo);
+        *modInfo = requestedInfo.makeAlphaType(kOpaque_SkAlphaType);
+    }
+    return kSuccess;
+}
diff --git a/src/codec/SkCodec_libpng.h b/src/codec/SkCodec_libpng.h
new file mode 100644 (file)
index 0000000..a5327dd
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec.h"
+#include "SkImageInfo.h"
+
+extern "C" {
+    // FIXME: I'd like to force all platforms to use the same decoder, but this
+    // means an extra dependency on Mac/Win.
+    #include "png.h"
+}
+
+class SkStream;
+
+class SkPngCodec : public SkCodec {
+public:
+    // Assumes IsPng was called and returned true.
+    static SkCodec* NewFromStream(SkStream*);
+    static bool IsPng(SkStream*);
+protected:
+    Result onGetPixels(const SkImageInfo&, void*, size_t, SkPMColor*, int*) SK_OVERRIDE;
+private:
+    png_structp             fPng_ptr;
+    png_infop               fInfo_ptr;
+
+    SkPngCodec(const SkImageInfo&, SkStream*, png_structp, png_infop);
+    ~SkPngCodec();
+
+    typedef SkCodec INHERITED;
+};
diff --git a/src/codec/SkSwizzler.cpp b/src/codec/SkSwizzler.cpp
new file mode 100644 (file)
index 0000000..563933f
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorPriv.h"
+#include "SkSwizzler.h"
+#include "SkTemplates.h"
+
+// index
+
+#define A32_MASK_IN_PLACE   (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT)
+
+static bool swizzle_index_to_n32(void* SK_RESTRICT dstRow,
+                                 const uint8_t* SK_RESTRICT src,
+                                 int width, int deltaSrc, int, const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    SkPMColor cc = A32_MASK_IN_PLACE;
+    for (int x = 0; x < width; x++) {
+        SkPMColor c = ctable[*src];
+        cc &= c;
+        dst[x] = c;
+        src += deltaSrc;
+    }
+    return cc != A32_MASK_IN_PLACE;
+}
+
+static bool swizzle_index_to_n32_skipZ(void* SK_RESTRICT dstRow,
+                                      const uint8_t* SK_RESTRICT src,
+                                      int width, int deltaSrc, int,
+                                      const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    SkPMColor cc = A32_MASK_IN_PLACE;
+    for (int x = 0; x < width; x++) {
+        SkPMColor c = ctable[*src];
+        cc &= c;
+        if (c != 0) {
+            dst[x] = c;
+        }
+        src += deltaSrc;
+    }
+    return cc != A32_MASK_IN_PLACE;
+}
+
+#undef A32_MASK_IN_PLACE
+
+// n32
+static bool swizzle_rgbx_to_n32(void* SK_RESTRICT dstRow,
+                                const uint8_t* SK_RESTRICT src,
+                                int width, int deltaSrc, int, const SkPMColor[]) {
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool swizzle_rgba_to_n32_premul(void* SK_RESTRICT dstRow,
+                                       const uint8_t* SK_RESTRICT src,
+                                       int width, int deltaSrc, int, const SkPMColor[]) {
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    unsigned alphaMask = 0xFF;
+    for (int x = 0; x < width; x++) {
+        unsigned alpha = src[3];
+        dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
+        src += deltaSrc;
+        alphaMask &= alpha;
+    }
+    return alphaMask != 0xFF;
+}
+
+static bool swizzle_rgba_to_n32_unpremul(void* SK_RESTRICT dstRow,
+                                         const uint8_t* SK_RESTRICT src,
+                                         int width, int deltaSrc, int,
+                                         const SkPMColor[]) {
+    uint32_t* SK_RESTRICT dst = reinterpret_cast<uint32_t*>(dstRow);
+    unsigned alphaMask = 0xFF;
+    for (int x = 0; x < width; x++) {
+        unsigned alpha = src[3];
+        dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]);
+        src += deltaSrc;
+        alphaMask &= alpha;
+    }
+    return alphaMask != 0xFF;
+}
+
+static bool swizzle_rgba_to_n32_premul_skipZ(void* SK_RESTRICT dstRow,
+                                             const uint8_t* SK_RESTRICT src,
+                                             int width, int deltaSrc, int,
+                                             const SkPMColor[]) {
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    unsigned alphaMask = 0xFF;
+    for (int x = 0; x < width; x++) {
+        unsigned alpha = src[3];
+        if (0 != alpha) {
+            dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
+        }
+        src += deltaSrc;
+        alphaMask &= alpha;
+    }
+    return alphaMask != 0xFF;
+}
+
+/**
+    FIXME: This was my idea to cheat in order to continue taking advantage of skipping zeroes.
+    This would be fine for drawing normally, but not for drawing with transfer modes. Being
+    honest means we can draw correctly with transfer modes, with the cost of not being able
+    to take advantage of Android's free unwritten pages. Something to keep in mind when we
+    decide whether to switch to unpremul default.
+static bool swizzle_rgba_to_n32_unpremul_skipZ(void* SK_RESTRICT dstRow,
+                                               const uint8_t* SK_RESTRICT src,
+                                               int width, int deltaSrc, int,
+                                               const SkPMColor[]) {
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    unsigned alphaMask = 0xFF;
+    for (int x = 0; x < width; x++) {
+        unsigned alpha = src[3];
+        // NOTE: We cheat here. The caller requested unpremul and skip zeroes. It's possible
+        // the color components are not zero, but we skip them anyway, meaning they'll remain
+        // zero (implied by the request to skip zeroes).
+        if (0 != alpha) {
+            dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]);
+        }
+        src += deltaSrc;
+        alphaMask &= alpha;
+    }
+    return alphaMask != 0xFF;
+}
+*/
+
+SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, const SkPMColor* ctable,
+                                       const SkImageInfo& info, void* dst,
+                                       size_t dstRowBytes, bool skipZeroes) {
+    if (info.colorType() == kUnknown_SkColorType) {
+        return NULL;
+    }
+    if (info.minRowBytes() > dstRowBytes) {
+        return  NULL;
+    }
+    if (kIndex == sc && NULL == ctable) {
+        return NULL;
+    }
+    RowProc proc = NULL;
+    switch (sc) {
+        case kIndex:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    // We assume the color premultiplied ctable (or not) as desired.
+                    if (skipZeroes) {
+                        proc = &swizzle_index_to_n32_skipZ;
+                    } else {
+                        proc = &swizzle_index_to_n32;
+                    }
+                    break;
+                    
+                default:
+                    break;
+            }
+            break;
+        case kRGBX:
+            // TODO: Support other swizzles.
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    proc = &swizzle_rgbx_to_n32;
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case kRGBA:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    if (info.alphaType() == kUnpremul_SkAlphaType) {
+                        // Respect skipZeroes?
+                        proc = &swizzle_rgba_to_n32_unpremul;
+                    } else {
+                        if (skipZeroes) {
+                            proc = &swizzle_rgba_to_n32_premul_skipZ;
+                        } else {
+                            proc = &swizzle_rgba_to_n32_premul;
+                        }
+                    }
+                    break;
+                default:
+                    break;
+            }
+            break;
+        default:
+            break;
+    }
+    if (NULL == proc) {
+        return NULL;
+    }
+    return SkNEW_ARGS(SkSwizzler, (proc, ctable, BytesPerPixel(sc), info, dst, dstRowBytes));
+}
+
+SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcBpp,
+                       const SkImageInfo& info, void* dst, size_t rowBytes)
+    : fRowProc(proc)
+    , fColorTable(ctable)
+    , fSrcPixelSize(srcBpp)
+    , fDstInfo(info)
+    , fDstRow(dst)
+    , fDstRowBytes(rowBytes)
+    , fCurrY(0)
+{
+}
+
+bool SkSwizzler::next(const uint8_t* SK_RESTRICT src) {
+    SkASSERT(fCurrY < fDstInfo.height());
+    const bool hadAlpha = fRowProc(fDstRow, src, fDstInfo.width(), fSrcPixelSize,
+                                   fCurrY, fColorTable);
+    fCurrY++;
+    fDstRow = SkTAddOffset<void>(fDstRow, fDstRowBytes);
+    return hadAlpha;
+}
diff --git a/src/codec/SkSwizzler.h b/src/codec/SkSwizzler.h
new file mode 100644 (file)
index 0000000..0bf2ee3
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSwizzler_DEFINED
+#define SkSwizzler_DEFINED
+
+#include "SkTypes.h"
+#include "SkColor.h"
+#include "SkImageInfo.h"
+
+class SkSwizzler : public SkNoncopyable {
+public:
+    /**
+     *  Enum describing the config of the source data.
+     */
+    enum SrcConfig {
+        kGray,  // 1 byte per pixel
+        kIndex, // 1 byte per pixel
+        kRGB,   // 3 bytes per pixel
+        kRGBX,  // 4 byes per pixel (ignore 4th)
+        kRGBA,  // 4 bytes per pixel
+        kRGB_565 // 2 bytes per pixel
+    };
+
+    static int BytesPerPixel(SrcConfig sc) {
+        switch (sc) {
+            case kGray:
+            case kIndex:
+                return 1;
+            case kRGB:
+                return 3;
+            case kRGBX:
+            case kRGBA:
+                return 4;
+            case kRGB_565:
+                return 2;
+            default:
+                SkDebugf("invalid source config passed to BytesPerPixel\n");
+                return -1;
+        }
+    }
+
+    /**
+     *  Create a new SkSwizzler.
+     *  @param sc SrcConfig
+     *  @param info dimensions() describe both the src and the dst.
+     *              Other fields describe the dst.
+     *  @param dst Destination to write pixels. Must match info and dstRowBytes
+     *  @param dstRowBytes rowBytes for dst.
+     *  @param skipZeroes Whether to skip writing zeroes. Useful if dst is
+     *              zero-initialized. The implementation may or may not respect this.
+     *  @return A new SkSwizzler or NULL on failure.
+     */
+    static SkSwizzler* CreateSwizzler(SrcConfig sc, const SkPMColor* ctable,
+                                      const SkImageInfo& info, void* dst,
+                                      size_t dstRowBytes, bool skipZeroes);
+    /**
+     *  Swizzle the next line. Call height times, once for each row of source.
+     *  @param src The next row of the source data.
+     *  @return Whether the row had non-opaque alpha.
+     */
+    bool next(const uint8_t* SK_RESTRICT src);
+private:
+    /**
+     *  Method for converting raw data to Skia pixels.
+     *  @param dstRow Row in which to write the resulting pixels.
+     *  @param src Row of src data, in format specified by SrcConfig
+     *  @param width Width in pixels
+     *  @param bpp bytes per pixel of the source.
+     *  @param y Line of source.
+     *  @param ctable Colors (used for kIndex source).
+     */
+    typedef bool (*RowProc)(void* SK_RESTRICT dstRow,
+                            const uint8_t* SK_RESTRICT src,
+                            int width, int bpp, int y,
+                            const SkPMColor ctable[]);
+
+    const RowProc       fRowProc;
+    const SkPMColor*    fColorTable;    // Unowned pointer
+    const int           fSrcPixelSize;
+    const SkImageInfo   fDstInfo;
+    void*               fDstRow;
+    const size_t        fDstRowBytes;
+    int                 fCurrY;
+
+    SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcBpp,
+               const SkImageInfo& info, void* dst, size_t rowBytes);
+
+};
+#endif // SkSwizzler_DEFINED