aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/codec
diff options
context:
space:
mode:
authorGravatar scroggo <scroggo@google.com>2015-03-03 08:59:20 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2015-03-03 08:59:20 -0800
commitf24f2247c25b842327e12c70e44efe4cc1b28dfa (patch)
tree3dfa329ae0504ba31ed3e1fe6ee46b4db4f7a5c4 /src/codec
parent0aebf5d0d3a2aef38a71885c85303583fdeaad57 (diff)
Add SkCodec, including PNG implementation.
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. Requires a gclient sync to pull down libpng. BUG=skia:3257 Committed: https://skia.googlesource.com/skia/+/ca358852b4fed656d11107b2aaf28318a4518b49 (and then reverted) Review URL: https://codereview.chromium.org/930283002
Diffstat (limited to 'src/codec')
-rw-r--r--src/codec/SkCodec.cpp50
-rw-r--r--src/codec/SkCodec_libpng.cpp492
-rw-r--r--src/codec/SkCodec_libpng.h34
-rw-r--r--src/codec/SkSwizzler.cpp221
-rw-r--r--src/codec/SkSwizzler.h94
5 files changed, 891 insertions, 0 deletions
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
new file mode 100644
index 0000000000..ec36bc75e9
--- /dev/null
+++ b/src/codec/SkCodec.cpp
@@ -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
index 0000000000..8e7ee33a95
--- /dev/null
+++ b/src/codec/SkCodec_libpng.cpp
@@ -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
index 0000000000..a5327dda41
--- /dev/null
+++ b/src/codec/SkCodec_libpng.h
@@ -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
index 0000000000..563933f13a
--- /dev/null
+++ b/src/codec/SkSwizzler.cpp
@@ -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
index 0000000000..0bf2ee306a
--- /dev/null
+++ b/src/codec/SkSwizzler.h
@@ -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