Implementation of image decoding for bmp files, in accordance with the new API.
authormsarett <msarett@google.com>
Mon, 16 Mar 2015 15:27:53 +0000 (08:27 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 16 Mar 2015 15:27:53 +0000 (08:27 -0700)
Currently decodes to opaque and unpremul.

Tested on local test suite.

BUG=skia:3257

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

14 files changed:
dm/DMSrcSink.cpp
gyp/codec.gyp
include/codec/SkCodec.h
src/codec/SkCodec.cpp
src/codec/SkCodecPriv.h [new file with mode: 0644]
src/codec/SkCodec_libbmp.cpp [new file with mode: 0644]
src/codec/SkCodec_libbmp.h [new file with mode: 0644]
src/codec/SkCodec_libpng.cpp
src/codec/SkMaskSwizzler.cpp [new file with mode: 0644]
src/codec/SkMaskSwizzler.h [new file with mode: 0644]
src/codec/SkMasks.cpp [new file with mode: 0644]
src/codec/SkMasks.h [new file with mode: 0644]
src/codec/SkSwizzler.cpp
src/codec/SkSwizzler.h

index 5ca66c50e207c4809f41396e907d354d4d78a17d..22dd5679e3ec8d8f2486d108081e013c30a58dd8 100644 (file)
@@ -140,15 +140,27 @@ Error ImageSrc::draw(SkCanvas* canvas) const {
 
 SkISize ImageSrc::size() const {
     SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
-    SkBitmap bitmap;
-    if (!encoded || !SkImageDecoder::DecodeMemory(encoded->data(),
-                                                  encoded->size(),
-                                                  &bitmap,
-                                                  kUnknown_SkColorType,
-                                                  SkImageDecoder::kDecodeBounds_Mode)) {
-        return SkISize::Make(0,0);
+    if (FLAGS_codec) {
+        SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
+        if (!codec) {
+            return SkISize::Make(0,0);
+        }
+        SkImageInfo info;
+        if (!codec->getInfo(&info)) {
+            return SkISize::Make(0,0);
+        }
+        return info.dimensions();
+    } else {
+        SkBitmap bitmap;
+        if (!encoded || !SkImageDecoder::DecodeMemory(encoded->data(),
+                                                      encoded->size(),
+                                                      &bitmap,
+                                                      kUnknown_SkColorType,
+                                                      SkImageDecoder::kDecodeBounds_Mode)) {
+            return SkISize::Make(0,0);
+        }
+        return bitmap.dimensions();
     }
-    return bitmap.dimensions();
 }
 
 Name ImageSrc::name() const {
index b9627995ef27a163f916ee25f93d5b984acde6ae..ce601cca7d1077ba5e82747bb19d7b11466f4fe6 100644 (file)
@@ -17,6 +17,9 @@
       'sources': [
         '../src/codec/SkCodec.cpp',
         '../src/codec/SkCodec_libpng.cpp',
+        '../src/codec/SkCodec_libbmp.cpp',
+        '../src/codec/SkMaskSwizzler.cpp',
+        '../src/codec/SkMasks.cpp',
         '../src/codec/SkSwizzler.cpp',
       ],
       'direct_dependent_settings': {
index beb9cb97b32bdb51bed7cc4d76d24b968fe0858d..c46985268dce91cd766784c9cb878522572250d3 100644 (file)
@@ -84,6 +84,15 @@ protected:
      */
     bool SK_WARN_UNUSED_RESULT rewindIfNeeded();
 
+    /*
+     *
+     * Get method for the input stream
+     *
+     */
+    SkStream* stream() {
+        return fStream.get();
+    }
+
 private:
     const SkImageInfo fInfo;
     SkAutoTDelete<SkStream> fStream;
index ec36bc75e94e00edd22b857b179a9033a95fe5c1..9b0feae78fb9d724401c64b6dbc6e18261c5153c 100644 (file)
@@ -7,24 +7,33 @@
 
 #include "SkCodec.h"
 #include "SkData.h"
+#include "SkCodec_libbmp.h"
 #include "SkCodec_libpng.h"
 #include "SkStream.h"
 
+struct DecoderProc {
+    bool (*IsFormat)(SkStream*);
+    SkCodec* (*NewFromStream)(SkStream*);
+};
+
+static const DecoderProc gDecoderProcs[] = {
+    { SkPngCodec::IsPng, SkPngCodec::NewFromStream },
+    { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream }
+};
+
 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);
+    for (DecoderProc proc : gDecoderProcs) {
+        const bool correctFormat = proc.IsFormat(stream);
+        if (!stream->rewind()) {
+            return NULL;
+        }
+        if (correctFormat) {
+            return proc.NewFromStream(stream);
+        }
     }
-    // TODO: Check other image types.
     return NULL;
 }
 
diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h
new file mode 100644 (file)
index 0000000..a0e5c94
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCodecPriv_DEFINED
+#define SkCodecPriv_DEFINED
+
+#include "SkImageInfo.h"
+#include "SkSwizzler.h"
+#include "SkTypes.h"
+
+/*
+ *
+ * Helper routine for alpha result codes
+ *
+ */
+#define INIT_RESULT_ALPHA                       \
+    uint8_t zeroAlpha = 0;                      \
+    uint8_t maxAlpha = 0xFF;
+
+#define UPDATE_RESULT_ALPHA(alpha)              \
+    zeroAlpha |= (alpha);                       \
+    maxAlpha  &= (alpha);
+
+#define COMPUTE_RESULT_ALPHA                    \
+    SkSwizzler::GetResult(zeroAlpha, maxAlpha);
+
+/*
+ *
+ * Compute row bytes for an image using pixels per byte
+ *
+ */
+static inline size_t compute_row_bytes_ppb(int width, uint32_t pixelsPerByte) {
+    return (width + pixelsPerByte - 1) / pixelsPerByte;
+}
+
+/*
+ *
+ * Compute row bytes for an image using bytes per pixel
+ *
+ */
+static inline size_t compute_row_bytes_bpp(int width, uint32_t bytesPerPixel) {
+    return width * bytesPerPixel;
+}
+
+/*
+ *
+ * Compute row bytes for an image
+ *
+ */
+static inline size_t compute_row_bytes(int width, uint32_t bitsPerPixel) {
+    if (bitsPerPixel < 16) {
+        SkASSERT(0 == 8 % bitsPerPixel);
+        const uint32_t pixelsPerByte = 8 / bitsPerPixel;
+        return compute_row_bytes_ppb(width, pixelsPerByte);
+    } else {
+        SkASSERT(0 == bitsPerPixel % 8);
+        const uint32_t bytesPerPixel = bitsPerPixel / 8;
+        return compute_row_bytes_bpp(width, bytesPerPixel);
+    }
+}
+
+/*
+ *
+ * Checks if alpha types are premul and unpremul
+ *
+ */
+static inline bool premul_and_unpremul(SkAlphaType dst, SkAlphaType src) {
+    return kPremul_SkAlphaType == dst && kUnpremul_SkAlphaType == src;
+}
+
+/*
+ *
+ * Get a byte from a buffer
+ * This method is unsafe, the caller is responsible for performing a check
+ *
+ */
+static inline uint8_t get_byte(uint8_t* buffer, uint32_t i) {
+    return buffer[i];
+}
+
+/*
+ *
+ * Get a short from a buffer
+ * This method is unsafe, the caller is responsible for performing a check
+ *
+ */
+static inline uint16_t get_short(uint8_t* buffer, uint32_t i) {
+    uint16_t result;
+    memcpy(&result, &(buffer[i]), 2);
+#ifdef SK_CPU_BENDIAN
+    return SkEndianSwap16(result);
+#else
+    return result;
+#endif
+}
+
+/*
+ *
+ * Get an int from a buffer
+ * This method is unsafe, the caller is responsible for performing a check
+ *
+ */
+static inline uint32_t get_int(uint8_t* buffer, uint32_t i) {
+    uint32_t result;
+    memcpy(&result, &(buffer[i]), 4);
+#ifdef SK_CPU_BENDIAN
+    return SkEndianSwap32(result);
+#else
+    return result;
+#endif
+}
+
+#endif // SkCodecPriv_DEFINED
diff --git a/src/codec/SkCodec_libbmp.cpp b/src/codec/SkCodec_libbmp.cpp
new file mode 100644 (file)
index 0000000..5b9691c
--- /dev/null
@@ -0,0 +1,903 @@
+/*
+ * 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_libbmp.h"
+#include "SkCodecPriv.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+
+/*
+ *
+ * Checks if the conversion between the input image and the requested output
+ * image has been implemented
+ *
+ */
+static bool conversion_possible(const SkImageInfo& dst,
+                                const SkImageInfo& src) {
+    // All of the swizzles convert to kN32
+    // TODO: Update this when more swizzles are supported
+    if (kN32_SkColorType != dst.colorType()) {
+        return false;
+    }
+    // Support the swizzle if the requested alpha type is the same as our guess
+    // for the input alpha type
+    if (src.alphaType() == dst.alphaType()) {
+        return true;
+    }
+    // TODO: Support more swizzles, especially premul
+    return false;
+}
+
+/*
+ *
+ * Defines the version and type of the second bitmap header
+ *
+ */
+enum BitmapHeaderType {
+    kInfoV1_BitmapHeaderType,
+    kInfoV2_BitmapHeaderType,
+    kInfoV3_BitmapHeaderType,
+    kInfoV4_BitmapHeaderType,
+    kInfoV5_BitmapHeaderType,
+    kOS2V1_BitmapHeaderType,
+    kOS2VX_BitmapHeaderType,
+    kUnknown_BitmapHeaderType
+};
+
+/*
+ *
+ * Possible bitmap compression types
+ *
+ */
+enum BitmapCompressionMethod {
+    kNone_BitmapCompressionMethod =          0,
+    k8BitRLE_BitmapCompressionMethod =       1,
+    k4BitRLE_BitmapCompressionMethod =       2,
+    kBitMasks_BitmapCompressionMethod =      3,
+    kJpeg_BitmapCompressionMethod =          4,
+    kPng_BitmapCompressionMethod =           5,
+    kAlphaBitMasks_BitmapCompressionMethod = 6,
+    kCMYK_BitmapCompressionMethod =          11,
+    kCMYK8BitRLE_BitmapCompressionMethod =   12,
+    kCMYK4BitRLE_BitmapCompressionMethod =   13
+};
+
+/*
+ *
+ * Checks the start of the stream to see if the image is a bitmap
+ *
+ */
+bool SkBmpCodec::IsBmp(SkStream* stream) {
+    // TODO: Support "IC", "PT", "CI", "CP", "BA"
+    // TODO: ICO files may contain a BMP and need to use this decoder
+    const char bmpSig[] = { 'B', 'M' };
+    char buffer[sizeof(bmpSig)];
+    return stream->read(buffer, sizeof(bmpSig)) == sizeof(bmpSig) &&
+            !memcmp(buffer, bmpSig, sizeof(bmpSig));
+}
+
+/*
+ *
+ * Assumes IsBmp was called and returned true
+ * Creates a bitmap decoder
+ * Reads enough of the stream to determine the image format
+ *
+ */
+SkCodec* SkBmpCodec::NewFromStream(SkStream* stream) {
+    // Header size constants
+    static const uint32_t kBmpHeaderBytes = 14;
+    static const uint32_t kBmpHeaderBytesPlusFour = kBmpHeaderBytes + 4;
+    static const uint32_t kBmpOS2V1Bytes = 12;
+    static const uint32_t kBmpOS2V2Bytes = 64;
+    static const uint32_t kBmpInfoBaseBytes = 16;
+    static const uint32_t kBmpInfoV1Bytes = 40;
+    static const uint32_t kBmpInfoV2Bytes = 52;
+    static const uint32_t kBmpInfoV3Bytes = 56;
+    static const uint32_t kBmpInfoV4Bytes = 108;
+    static const uint32_t kBmpInfoV5Bytes = 124;
+    static const uint32_t kBmpMaskBytes = 12;
+
+    // Read the first header and the size of the second header
+    SkAutoTDeleteArray<uint8_t> hBuffer(
+            SkNEW_ARRAY(uint8_t, kBmpHeaderBytesPlusFour));
+    if (stream->read(hBuffer.get(), kBmpHeaderBytesPlusFour) !=
+            kBmpHeaderBytesPlusFour) {
+        SkDebugf("Error: unable to read first bitmap header.\n");
+        return NULL;
+    }
+
+    // The total bytes in the bmp file
+    // We only need to use this value for RLE decoding, so we will only check
+    // that it is valid in the RLE case.
+    const uint32_t totalBytes = get_int(hBuffer.get(), 2);
+
+    // The offset from the start of the file where the pixel data begins
+    const uint32_t offset = get_int(hBuffer.get(), 10);
+    if (offset < kBmpHeaderBytes + kBmpOS2V1Bytes) {
+        SkDebugf("Error: invalid starting location for pixel data\n");
+        return NULL;
+    }
+
+    // The size of the second (info) header in bytes
+    // The size is the first field of the second header, so we have already
+    // read the first four infoBytes.
+    const uint32_t infoBytes = get_int(hBuffer.get(), 14);
+    if (infoBytes < kBmpOS2V1Bytes) {
+        SkDebugf("Error: invalid second header size.\n");
+        return NULL;
+    }
+    const uint32_t infoBytesRemaining = infoBytes - 4;
+    hBuffer.free();
+
+    // Read the second header
+    SkAutoTDeleteArray<uint8_t> iBuffer(
+            SkNEW_ARRAY(uint8_t, infoBytesRemaining));
+    if (stream->read(iBuffer.get(), infoBytesRemaining) != infoBytesRemaining) {
+        SkDebugf("Error: unable to read second bitmap header.\n");
+        return NULL;
+    }
+
+    // The number of bits used per pixel in the pixel data
+    uint16_t bitsPerPixel;
+
+    // The compression method for the pixel data
+    uint32_t compression = kNone_BitmapCompressionMethod;
+
+    // Number of colors in the color table, defaults to 0 or max (see below)
+    uint32_t numColors = 0;
+
+    // Bytes per color in the color table, early versions use 3, most use 4
+    uint32_t bytesPerColor;
+
+    // The image width and height
+    int width, height;
+
+    // Determine image information depending on second header format
+    BitmapHeaderType headerType;
+    if (infoBytes >= kBmpInfoBaseBytes) {
+        // Check the version of the header
+        switch (infoBytes) {
+            case kBmpInfoV1Bytes:
+                headerType = kInfoV1_BitmapHeaderType;
+                break;
+            case kBmpInfoV2Bytes:
+                headerType = kInfoV2_BitmapHeaderType;
+                break;
+            case kBmpInfoV3Bytes:
+                headerType = kInfoV3_BitmapHeaderType;
+                break;
+            case kBmpInfoV4Bytes:
+                headerType = kInfoV4_BitmapHeaderType;
+                break;
+            case kBmpInfoV5Bytes:
+                headerType = kInfoV5_BitmapHeaderType;
+                break;
+            case 16:
+            case 20:
+            case 24:
+            case 28:
+            case 32:
+            case 36:
+            case 42:
+            case 46:
+            case 48:
+            case 60:
+            case kBmpOS2V2Bytes:
+                headerType = kOS2VX_BitmapHeaderType;
+                break;
+            default:
+                // We do not signal an error here because there is the
+                // possibility of new or undocumented bmp header types.  Most
+                // of the newer versions of bmp headers are similar to and
+                // build off of the older versions, so we may still be able to
+                // decode the bmp.
+                SkDebugf("Warning: unknown bmp header format.\n");
+                headerType = kUnknown_BitmapHeaderType;
+                break;
+        }
+        // We check the size of the header before entering the if statement.
+        // We should not reach this point unless the size is large enough for
+        // these required fields.
+        SkASSERT(infoBytesRemaining >= 12);
+        width = get_int(iBuffer.get(), 0);
+        height = get_int(iBuffer.get(), 4);
+        bitsPerPixel = get_short(iBuffer.get(), 10);
+
+        // Some versions do not have these fields, so we check before
+        // overwriting the default value.
+        if (infoBytesRemaining >= 16) {
+            compression = get_int(iBuffer.get(), 12);
+            if (infoBytesRemaining >= 32) {
+                numColors = get_int(iBuffer.get(), 28);
+            }
+        }
+
+        // All of the headers that reach this point, store color table entries
+        // using 4 bytes per pixel.
+        bytesPerColor = 4;
+    } else if (infoBytes >= kBmpOS2V1Bytes) {
+        // The OS2V1 is treated separately because it has a unique format
+        headerType = kOS2V1_BitmapHeaderType;
+        width = (int) get_short(iBuffer.get(), 0);
+        height = (int) get_short(iBuffer.get(), 2);
+        bitsPerPixel = get_short(iBuffer.get(), 6);
+        bytesPerColor = 3;
+    } else {
+        // There are no valid bmp headers
+        SkDebugf("Error: second bitmap header size is invalid.\n");
+        return NULL;
+    }
+
+    // Check for valid dimensions from header
+    RowOrder rowOrder = kBottomUp_RowOrder;
+    if (height < 0) {
+        height = -height;
+        rowOrder = kTopDown_RowOrder;
+    }
+    static const int kBmpMaxDim = 1 << 16;
+    if (width < 0 || width >= kBmpMaxDim || height >= kBmpMaxDim) {
+        // TODO: Decide if we want to support really large bmps.
+        SkDebugf("Error: invalid bitmap dimensions.\n");
+        return NULL;
+    }
+
+    // Create mask struct
+    SkMasks::InputMasks inputMasks;
+    memset(&inputMasks, 0, 4*sizeof(uint32_t));
+
+    // Determine the input compression format and set bit masks if necessary
+    uint32_t maskBytes = 0;
+    BitmapInputFormat inputFormat = kUnknown_BitmapInputFormat;
+    switch (compression) {
+        case kNone_BitmapCompressionMethod:
+            inputFormat = kStandard_BitmapInputFormat;
+            break;
+        case k8BitRLE_BitmapCompressionMethod:
+            if (bitsPerPixel != 8) {
+                SkDebugf("Warning: correcting invalid bitmap format.\n");
+                bitsPerPixel = 8;
+            }
+            inputFormat = kRLE_BitmapInputFormat;
+            break;
+        case k4BitRLE_BitmapCompressionMethod:
+            if (bitsPerPixel != 4) {
+                SkDebugf("Warning: correcting invalid bitmap format.\n");
+                bitsPerPixel = 4;
+            }
+            inputFormat = kRLE_BitmapInputFormat;
+            break;
+        case kAlphaBitMasks_BitmapCompressionMethod:
+        case kBitMasks_BitmapCompressionMethod:
+            // Load the masks
+            inputFormat = kBitMask_BitmapInputFormat;
+            switch (headerType) {
+                case kInfoV1_BitmapHeaderType: {
+                    // The V1 header stores the bit masks after the header
+                    SkAutoTDeleteArray<uint8_t> mBuffer(
+                            SkNEW_ARRAY(uint8_t, kBmpMaskBytes));
+                    if (stream->read(mBuffer.get(), kBmpMaskBytes) !=
+                            kBmpMaskBytes) {
+                        SkDebugf("Error: unable to read bit inputMasks.\n");
+                        return NULL;
+                    }
+                    maskBytes = kBmpMaskBytes;
+                    inputMasks.red = get_int(mBuffer.get(), 0);
+                    inputMasks.green = get_int(mBuffer.get(), 4);
+                    inputMasks.blue = get_int(mBuffer.get(), 8);
+                    break;
+                }
+                case kInfoV2_BitmapHeaderType:
+                case kInfoV3_BitmapHeaderType:
+                case kInfoV4_BitmapHeaderType:
+                case kInfoV5_BitmapHeaderType:
+                    // Header types are matched based on size.  If the header
+                    // is V2+, we are guaranteed to be able to read at least
+                    // this size.
+                    SkASSERT(infoBytesRemaining >= 48);
+                    inputMasks.red = get_int(iBuffer.get(), 36);
+                    inputMasks.green = get_int(iBuffer.get(), 40);
+                    inputMasks.blue = get_int(iBuffer.get(), 44);
+                    break;
+                case kOS2VX_BitmapHeaderType:
+                    // TODO: Decide if we intend to support this.
+                    //       It is unsupported in the previous version and
+                    //       in chromium.  I have not come across a test case
+                    //       that uses this format.
+                    SkDebugf("Error: huffman format unsupported.\n");
+                    return NULL;
+                default:
+                   SkDebugf("Error: invalid bmp bit masks header.\n");
+                   return NULL;
+            }
+            break;
+        case kJpeg_BitmapCompressionMethod:
+            if (24 == bitsPerPixel) {
+                inputFormat = kRLE_BitmapInputFormat;
+                break;
+            }
+            // Fall through
+        case kPng_BitmapCompressionMethod:
+            // TODO: Decide if we intend to support this.
+            //       It is unsupported in the previous version and
+            //       in chromium.  I think it is used mostly for printers.
+            SkDebugf("Error: compression format not supported.\n");
+            return NULL;
+        case kCMYK_BitmapCompressionMethod:
+        case kCMYK8BitRLE_BitmapCompressionMethod:
+        case kCMYK4BitRLE_BitmapCompressionMethod:
+            // TODO: Same as above.
+            SkDebugf("Error: CMYK not supported for bitmap decoding.\n");
+            return NULL;
+        default:
+            SkDebugf("Error: invalid format for bitmap decoding.\n");
+            return NULL;
+    }
+
+    // Most versions of bmps should be rendered as opaque.  Either they do
+    // not have an alpha channel, or they expect the alpha channel to be
+    // ignored.  V4+ bmp files introduce an alpha mask and allow the creator
+    // of the image to use the alpha channels.  However, many of these images
+    // leave the alpha channel blank and expect to be rendered as opaque.  For
+    // this reason, we set the alpha type to kUnknown for V4+ bmps and figure
+    // out the alpha type during the decode.
+    SkAlphaType alphaType = kOpaque_SkAlphaType;
+    if (kInfoV4_BitmapHeaderType == headerType ||
+            kInfoV5_BitmapHeaderType == headerType) {
+        // Header types are matched based on size.  If the header is
+        // V4+, we are guaranteed to be able to read at least this size.
+        SkASSERT(infoBytesRemaining > 52);
+        inputMasks.alpha = get_int(iBuffer.get(), 48);
+        if (inputMasks.alpha != 0) {
+            alphaType = kUnpremul_SkAlphaType;
+        }
+    }
+    iBuffer.free();
+
+    // Check for valid bits per pixel input
+    switch (bitsPerPixel) {
+        // In addition to more standard pixel compression formats, bmp supports
+        // the use of bit masks to determine pixel components.  The standard
+        // format for representing 16-bit colors is 555 (XRRRRRGGGGGBBBBB),
+        // which does not map well to any Skia color formats.  For this reason,
+        // we will always enable mask mode with 16 bits per pixel.
+        case 16:
+            if (kBitMask_BitmapInputFormat != inputFormat) {
+                inputMasks.red = 0x7C00;
+                inputMasks.green = 0x03E0;
+                inputMasks.blue = 0x001F;
+                inputFormat = kBitMask_BitmapInputFormat;
+            }
+            break;
+        case 1:
+        case 2:
+        case 4:
+        case 8:
+        case 24:
+        case 32:
+            break;
+        default:
+            SkDebugf("Error: invalid input value for bits per pixel.\n");
+            return NULL;
+    }
+
+    // Check that input bit masks are valid and create the masks object
+    SkAutoTDelete<SkMasks>
+            masks(SkMasks::CreateMasks(inputMasks, bitsPerPixel));
+    if (NULL == masks) {
+        SkDebugf("Error: invalid input masks.\n");
+        return NULL;
+    }
+
+    // Process the color table
+    uint32_t colorBytes = 0;
+    SkPMColor* colorTable = NULL;
+    if (bitsPerPixel < 16) {
+        // Verify the number of colors for the color table
+        const uint32_t maxColors = 1 << bitsPerPixel;
+        // Zero is a default for maxColors
+        // Also set numColors to maxColors when input is too large
+        if (numColors <= 0 || numColors > maxColors) {
+            numColors = maxColors;
+        }
+        colorTable = SkNEW_ARRAY(SkPMColor, maxColors);
+
+        // Construct the color table
+        colorBytes = numColors * bytesPerColor;
+        SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes));
+        if (stream->read(cBuffer.get(), colorBytes) != colorBytes) {
+            SkDebugf("Error: unable to read color table.\n");
+            return NULL;
+        }
+
+        // Fill in the color table (colors are stored unpremultiplied)
+        uint32_t i = 0;
+        for (; i < numColors; i++) {
+            uint8_t blue = get_byte(cBuffer.get(), i*bytesPerColor);
+            uint8_t green = get_byte(cBuffer.get(), i*bytesPerColor + 1);
+            uint8_t red = get_byte(cBuffer.get(), i*bytesPerColor + 2);
+            uint8_t alpha = 0xFF;
+            if (kOpaque_SkAlphaType != alphaType) {
+                alpha = (inputMasks.alpha >> 24) &
+                        get_byte(cBuffer.get(), i*bytesPerColor + 3);
+            }
+            // Store the unpremultiplied color
+            colorTable[i] = SkPackARGB32NoCheck(alpha, red, green, blue);
+        }
+
+        // To avoid segmentation faults on bad pixel data, fill the end of the
+        // color table with black.  This is the same the behavior as the
+        // chromium decoder.
+        for (; i < maxColors; i++) {
+            colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
+        }
+    }
+
+    // Ensure that the stream now points to the start of the pixel array
+    uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes + colorBytes;
+
+    // Check that we have not read past the pixel array offset
+    if(bytesRead > offset) {
+        // This may occur on OS 2.1 and other old versions where the color
+        // table defaults to max size, and the bmp tries to use a smaller color
+        // table.  This is invalid, and our decision is to indicate an error,
+        // rather than try to guess the intended size of the color table and
+        // rewind the stream to display the image.
+        SkDebugf("Error: pixel data offset less than header size.\n");
+        return NULL;
+    }
+
+    // Skip to the start of the pixel array
+    if (stream->skip(offset - bytesRead) != offset - bytesRead) {
+        SkDebugf("Error: unable to skip to image data.\n");
+        return NULL;
+    }
+
+    // Remaining bytes is only used for RLE
+    const int remainingBytes = totalBytes - offset;
+    if (remainingBytes <= 0 && kRLE_BitmapInputFormat == inputFormat) {
+        SkDebugf("Error: RLE requires valid input size.\n");
+        return NULL;
+    }
+
+    // Return the codec
+    // We will use ImageInfo to store width, height, and alpha type.  We will
+    // choose kN32_SkColorType as the input color type because that is the
+    // expected choice for a destination color type.  In reality, the input
+    // color type has many possible formats.
+    const SkImageInfo& imageInfo = SkImageInfo::Make(width, height,
+            kN32_SkColorType, alphaType);
+    return SkNEW_ARGS(SkBmpCodec, (imageInfo, stream, bitsPerPixel,
+                                   inputFormat, masks.detach(), colorTable,
+                                   rowOrder, remainingBytes));
+}
+
+/*
+ *
+ * Creates an instance of the decoder
+ * Called only by NewFromStream
+ *
+ */
+SkBmpCodec::SkBmpCodec(const SkImageInfo& info, SkStream* stream,
+                       uint16_t bitsPerPixel, BitmapInputFormat inputFormat,
+                       SkMasks* masks, SkPMColor* colorTable,
+                       RowOrder rowOrder,
+                       const uint32_t remainingBytes)
+    : INHERITED(info, stream)
+    , fBitsPerPixel(bitsPerPixel)
+    , fInputFormat(inputFormat)
+    , fMasks(masks)
+    , fColorTable(colorTable)
+    , fRowOrder(rowOrder)
+    , fRemainingBytes(remainingBytes)
+{}
+
+/*
+ *
+ * Initiates the bitmap decode
+ *
+ */
+SkCodec::Result SkBmpCodec::onGetPixels(const SkImageInfo& dstInfo,
+                                        void* dst, size_t dstRowBytes,
+                                        SkPMColor*, int*) {
+    if (!this->rewindIfNeeded()) {
+        return kCouldNotRewind;
+    }
+    if (dstInfo.dimensions() != this->getOriginalInfo().dimensions()) {
+        SkDebugf("Error: scaling not supported.\n");
+        return kInvalidScale;
+    }
+    if (!conversion_possible(dstInfo, this->getOriginalInfo())) {
+        SkDebugf("Error: cannot convert input type to output type.\n");
+        return kInvalidConversion;
+    }
+
+    switch (fInputFormat) {
+        case kBitMask_BitmapInputFormat:
+            return decodeMask(dstInfo, dst, dstRowBytes);
+        case kRLE_BitmapInputFormat:
+            return decodeRLE(dstInfo, dst, dstRowBytes);
+        case kStandard_BitmapInputFormat:
+            return decode(dstInfo, dst, dstRowBytes);
+        default:
+            SkASSERT(false);
+            return kInvalidInput;
+    }
+}
+
+/*
+ *
+ * Performs the bitmap decoding for bit masks input format
+ *
+ */
+SkCodec::Result SkBmpCodec::decodeMask(const SkImageInfo& dstInfo,
+                                       void* dst, size_t dstRowBytes) {
+    // Set constant values
+    const int width = dstInfo.width();
+    const int height = dstInfo.height();
+    const size_t rowBytes = SkAlign4(compute_row_bytes(width, fBitsPerPixel));
+
+    // Allocate space for a row buffer and a source for the swizzler
+    SkAutoTDeleteArray<uint8_t> srcBuffer(SkNEW_ARRAY(uint8_t, rowBytes));
+
+    // Get the destination start row and delta
+    SkPMColor* dstRow;
+    int delta;
+    if (kTopDown_RowOrder == fRowOrder) {
+        dstRow = (SkPMColor*) dst;
+        delta = (int) dstRowBytes;
+    } else {
+        dstRow = (SkPMColor*) SkTAddOffset<void>(dst, (height-1) * dstRowBytes);
+        delta = -((int) dstRowBytes);
+    }
+
+    // Create the swizzler
+    SkMaskSwizzler* swizzler = SkMaskSwizzler::CreateMaskSwizzler(
+            dstInfo, fMasks, fBitsPerPixel);
+
+    // Iterate over rows of the image
+    bool transparent = true;
+    for (int y = 0; y < height; y++) {
+        // Read a row of the input
+        if (stream()->read(srcBuffer.get(), rowBytes) != rowBytes) {
+            SkDebugf("Warning: incomplete input stream.\n");
+            return kIncompleteInput;
+        }
+
+        // Decode the row in destination format
+        SkSwizzler::ResultAlpha r = swizzler->next(dstRow, srcBuffer.get());
+        transparent &= SkSwizzler::IsTransparent(r);
+
+        // Move to the next row
+        dstRow = SkTAddOffset<SkPMColor>(dstRow, delta);
+    }
+
+    // Some fully transparent bmp images are intended to be opaque.  Here, we
+    // correct for this possibility.
+    dstRow = (SkPMColor*) dst;
+    if (transparent) {
+        for (int y = 0; y < height; y++) {
+            for (int x = 0; x < width; x++) {
+                dstRow[x] |= 0xFF000000;
+            }
+            dstRow = SkTAddOffset<SkPMColor>(dstRow, dstRowBytes);
+        }
+    }
+
+    // Finished decoding the entire image
+    return kSuccess;
+}
+
+/*
+ *
+ * Set an RLE pixel using the color table
+ *
+ */
+void SkBmpCodec::setRLEPixel(SkPMColor* dst, size_t dstRowBytes, int height,
+                             uint32_t x, uint32_t y, uint8_t index) {
+    if (kBottomUp_RowOrder == fRowOrder) {
+        y = height - y - 1;
+    }
+    SkPMColor* dstRow = SkTAddOffset<SkPMColor>(dst, y * dstRowBytes);
+    dstRow[x] = fColorTable.get()[index];
+}
+
+/*
+ *
+ * Performs the bitmap decoding for RLE input format
+ * RLE decoding is performed all at once, rather than a one row at a time
+ *
+ */
+SkCodec::Result SkBmpCodec::decodeRLE(const SkImageInfo& dstInfo,
+                                      void* dst, size_t dstRowBytes) {
+    // Set RLE flags
+    static const uint8_t RLE_ESCAPE = 0;
+    static const uint8_t RLE_EOL = 0;
+    static const uint8_t RLE_EOF = 1;
+    static const uint8_t RLE_DELTA = 2;
+
+    // Set constant values
+    const int width = dstInfo.width();
+    const int height = dstInfo.height();
+
+    // Input buffer parameters
+    uint32_t currByte = 0;
+    SkAutoTDeleteArray<uint8_t> buffer(SkNEW_ARRAY(uint8_t, fRemainingBytes));
+    size_t totalBytes = stream()->read(buffer.get(), fRemainingBytes);
+    if ((uint32_t) totalBytes < fRemainingBytes) {
+        SkDebugf("Warning: incomplete RLE file.\n");
+    } else if (totalBytes <= 0) {
+        SkDebugf("Error: could not read RLE image data.\n");
+        return kInvalidInput;
+    }
+
+    // Destination parameters
+    int x = 0;
+    int y = 0;
+    // If the code skips pixels, remaining pixels are transparent or black
+    // TODO: Skip this if memory was already zeroed.
+    memset(dst, 0, dstRowBytes * height);
+    SkPMColor* dstPtr = (SkPMColor*) dst;
+
+    while (true) {
+        // Every entry takes at least two bytes
+        if ((int) totalBytes - currByte < 2) {
+            SkDebugf("Warning: incomplete RLE input.\n");
+            return kIncompleteInput;
+        }
+
+        // Read the next two bytes.  These bytes have different meanings
+        // depending on their values.  In the first interpretation, the first
+        // byte is an escape flag and the second byte indicates what special
+        // task to perform.
+        const uint8_t flag = buffer.get()[currByte++];
+        const uint8_t task = buffer.get()[currByte++];
+
+        // If we have reached a row that is beyond the image size, and the RLE
+        // code does not indicate end of file, abort and signal a warning.
+        if (y >= height && (flag != RLE_ESCAPE || (task != RLE_EOF))) {
+            SkDebugf("Warning: invalid RLE input.\n");
+            return kIncompleteInput;
+        }
+
+        // Perform decoding
+        if (RLE_ESCAPE == flag) {
+            switch (task) {
+                case RLE_EOL:
+                    x = 0;
+                    y++;
+                    break;
+                case RLE_EOF:
+                    return kSuccess;
+                case RLE_DELTA: {
+                    // Two bytes are needed to specify delta
+                    if ((int) totalBytes - currByte < 2) {
+                        SkDebugf("Warning: incomplete RLE input\n");
+                        return kIncompleteInput;
+                    }
+                    // Modify x and y
+                    const uint8_t dx = buffer.get()[currByte++];
+                    const uint8_t dy = buffer.get()[currByte++];
+                    x += dx;
+                    y += dy;
+                    if (x > width || y > height) {
+                        SkDebugf("Warning: invalid RLE input.\n");
+                        return kIncompleteInput;
+                    }
+                    break;
+                }
+                default: {
+                    // If task does not match any of the above signals, it
+                    // indicates that we have a sequence of non-RLE pixels.
+                    // Furthermore, the value of task is equal to the number
+                    // of pixels to interpret.
+                    uint8_t numPixels = task;
+                    const size_t rowBytes = compute_row_bytes(numPixels,
+                            fBitsPerPixel);
+                    // Abort if setting numPixels moves us off the edge of the
+                    // image.  Also abort if there are not enough bytes
+                    // remaining in the stream to set numPixels.
+                    if (x + numPixels > width ||
+                            (int) totalBytes - currByte < SkAlign2(rowBytes)) {
+                        SkDebugf("Warning: invalid RLE input.\n");
+                        return kIncompleteInput;
+                    }
+                    // Set numPixels number of pixels
+                    SkPMColor* dstRow = SkTAddOffset<SkPMColor>(
+                            dstPtr, y * dstRowBytes);
+                    while (numPixels > 0) {
+                        switch(fBitsPerPixel) {
+                            case 4: {
+                                SkASSERT(currByte < totalBytes);
+                                uint8_t val = buffer.get()[currByte++];
+                                setRLEPixel(dstPtr, dstRowBytes, height, x++, y,
+                                        val >> 4);
+                                numPixels--;
+                                if (numPixels != 0) {
+                                    setRLEPixel(dstPtr, dstRowBytes, height,
+                                            x++, y, val & 0xF);
+                                    numPixels--;
+                                }
+                                break;
+                            }
+                            case 8:
+                                SkASSERT(currByte < totalBytes);
+                                setRLEPixel(dstPtr, dstRowBytes, height, x++, y,
+                                        buffer.get()[currByte++]);
+                                numPixels--;
+                                break;
+                            case 24: {
+                                SkASSERT(currByte + 2 < totalBytes);
+                                uint8_t blue = buffer.get()[currByte++];
+                                uint8_t green = buffer.get()[currByte++];
+                                uint8_t red = buffer.get()[currByte++];
+                                SkPMColor color = SkPackARGB32NoCheck(
+                                        0xFF, red, green, blue);
+                                dstRow[x++] = color;
+                                numPixels--;
+                            }
+                            default:
+                                SkASSERT(false);
+                                return kInvalidInput;
+                        }
+                    }
+                    // Skip a byte if necessary to maintain alignment
+                    if (!SkIsAlign2(rowBytes)) {
+                        currByte++;
+                    }
+                    break;
+                }
+            }
+        } else {
+            // If the first byte read is not a flag, it indicates the number of
+            // pixels to set in RLE mode.
+            const uint8_t numPixels = flag;
+            const int endX = SkTMin<int>(x + numPixels, width);
+
+            if (24 == fBitsPerPixel) {
+                // In RLE24, the second byte read is part of the pixel color.
+                // There are two more required bytes to finish encoding the
+                // color.
+                if ((int) totalBytes - currByte < 2) {
+                    SkDebugf("Warning: incomplete RLE input\n");
+                    return kIncompleteInput;
+                }
+
+                // Fill the pixels up to endX with the specified color
+                uint8_t blue = task;
+                uint8_t green = buffer.get()[currByte++];
+                uint8_t red = buffer.get()[currByte++];
+                SkPMColor color = SkPackARGB32NoCheck(0xFF, red, green, blue);
+                SkPMColor* dstRow =
+                        SkTAddOffset<SkPMColor>(dstPtr, y * dstRowBytes);
+                while (x < endX) {
+                    dstRow[x++] = color;
+                }
+            } else {
+                // In RLE8 or RLE4, the second byte read gives the index in the
+                // color table to look up the pixel color.
+                // RLE8 has one color index that gets repeated
+                // RLE4 has two color indexes in the upper and lower 4 bits of
+                // the bytes, which are alternated
+                uint8_t indices[2] = { task, task };
+                if (4 == fBitsPerPixel) {
+                    indices[0] >>= 4;
+                    indices[1] &= 0xf;
+                }
+
+                // Set the indicated number of pixels
+                for (int which = 0; x < endX; x++) {
+                    setRLEPixel(dstPtr, dstRowBytes, height, x, y,
+                            indices[which]);
+                    which = !which;
+                }
+            }
+        }
+    }
+}
+
+/*
+ *
+ * Performs the bitmap decoding for standard input format
+ *
+ */
+SkCodec::Result SkBmpCodec::decode(const SkImageInfo& dstInfo,
+                                   void* dst, size_t dstRowBytes) {
+    // Set constant values
+    const int width = dstInfo.width();
+    const int height = dstInfo.height();
+    const size_t rowBytes = SkAlign4(compute_row_bytes(width, fBitsPerPixel));
+    const uint32_t alphaMask = fMasks->getAlphaMask();
+
+    // Get swizzler configuration
+    SkSwizzler::SrcConfig config;
+    switch (fBitsPerPixel) {
+        case 1:
+            config = SkSwizzler::kIndex1;
+            break;
+        case 2:
+            config = SkSwizzler::kIndex2;
+            break;
+        case 4:
+            config = SkSwizzler::kIndex4;
+            break;
+        case 8:
+            config = SkSwizzler::kIndex;
+            break;
+        case 24:
+            config = SkSwizzler::kBGR;
+            break;
+        case 32:
+            if (0 == alphaMask) {
+                config = SkSwizzler::kBGRX;
+            } else {
+                config = SkSwizzler::kBGRA;
+            }
+            break;
+        default:
+            SkASSERT(false);
+            return kInvalidInput;
+    }
+
+    // Create swizzler
+    SkSwizzler* swizzler = SkSwizzler::CreateSwizzler(config, fColorTable.get(),
+            dstInfo, dst, dstRowBytes, false);
+
+    // Allocate space for a row buffer and a source for the swizzler
+    SkAutoTDeleteArray<uint8_t> srcBuffer(SkNEW_ARRAY(uint8_t, rowBytes));
+
+    // Iterate over rows of the image
+    // FIXME: bool transparent = true;
+    for (int y = 0; y < height; y++) {
+        // Read a row of the input
+        if (stream()->read(srcBuffer.get(), rowBytes) != rowBytes) {
+            SkDebugf("Warning: incomplete input stream.\n");
+            return kIncompleteInput;
+        }
+
+        // Decode the row in destination format
+        uint32_t row;
+        if (kTopDown_RowOrder == fRowOrder) {
+            row = y;
+        } else {
+            row = height - 1 - y;
+        }
+
+        swizzler->next(srcBuffer.get(), row);
+        // FIXME: SkSwizzler::ResultAlpha r =
+        //        swizzler->next(srcBuffer.get(), row);
+        // FIXME: transparent &= SkSwizzler::IsTransparent(r);
+    }
+
+    // FIXME: This code exists to match the behavior in the chromium decoder
+    // and to follow the bmp specification as it relates to alpha masks.  It is
+    // commented out because we have yet to discover a test image that provides
+    // an alpha mask and uses this decode mode.
+
+    // Now we adjust the output image with some additional behavior that
+    // SkSwizzler does not support.  Firstly, all bmp images that contain
+    // alpha are masked by the alpha mask.  Secondly, many fully transparent
+    // bmp images are intended to be opaque.  Here, we make those corrections.
+    // Modifying alpha is safe because colors are stored unpremultiplied.
+    /*
+    SkPMColor* dstRow = (SkPMColor*) dst;
+    if (SkSwizzler::kBGRA == config) {
+        for (int y = 0; y < height; y++) {
+            for (int x = 0; x < width; x++) {
+                if (transparent) {
+                    dstRow[x] |= 0xFF000000;
+                } else {
+                    dstRow[x] &= alphaMask;
+                }
+                dstRow = SkTAddOffset<SkPMColor>(dstRow, dstRowBytes);
+            }
+        }
+    }
+    */
+
+    // Finished decoding the entire image
+    return kSuccess;
+}
diff --git a/src/codec/SkCodec_libbmp.h b/src/codec/SkCodec_libbmp.h
new file mode 100644 (file)
index 0000000..f35b88d
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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"
+#include "SkMaskSwizzler.h"
+#include "SkStream.h"
+#include "SkSwizzler.h"
+#include "SkTypes.h"
+
+// TODO: rename SkCodec_libbmp files to SkBmpCodec
+// TODO: define a wrapper for SkDebugf that doesn't always print
+/*
+ *
+ * This class implements the decoding for bmp images
+ *
+ */
+class SkBmpCodec : public SkCodec {
+public:
+
+    /*
+     *
+     * Describes if rows of the input start at the top or bottom of the image
+     *
+     */
+    enum RowOrder {
+        kTopDown_RowOrder,
+        kBottomUp_RowOrder
+    };
+
+    /*
+     *
+     * Checks the start of the stream to see if the image is a bitmap
+     *
+     */
+    static bool IsBmp(SkStream*);
+
+    /*
+     *
+     * Assumes IsBmp was called and returned true
+     * Creates a bitmap decoder
+     * Reads enough of the stream to determine the image format
+     *
+     */
+    static SkCodec* NewFromStream(SkStream*);
+
+protected:
+
+    /*
+     *
+     * Initiates the bitmap decode
+     *
+     */
+    virtual Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
+                               size_t dstRowBytes, SkPMColor*,
+                               int*) SK_OVERRIDE;
+
+private:
+
+    /*
+     *
+     * Used to define the input format of the bitmap
+     *
+     */
+    enum BitmapInputFormat {
+        kStandard_BitmapInputFormat,
+        kRLE_BitmapInputFormat,
+        kBitMask_BitmapInputFormat,
+        kUnknown_BitmapInputFormat
+    };
+
+    /*
+     *
+     * Performs the bitmap decoding for bit masks input format
+     *
+     */
+    Result decodeMask(const SkImageInfo& dstInfo, void* dst,
+                      size_t dstRowBytes);
+
+    /*
+     *
+     * Set an RLE pixel using the color table
+     *
+     */
+    void setRLEPixel(SkPMColor* dst, size_t dstRowBytes, int height,
+                     uint32_t x, uint32_t y, uint8_t index);
+
+    /*
+     *
+     * Performs the bitmap decoding for RLE input format
+     *
+     */
+    Result decodeRLE(const SkImageInfo& dstInfo, void* dst,
+                     size_t dstRowBytes);
+
+    /*
+     *
+     * Performs the bitmap decoding for standard input format
+     *
+     */
+    Result decode(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes);
+
+    /*
+     *
+     * Creates an instance of the decoder
+     * Called only by NewFromStream
+     *
+     * @param srcInfo contains the source width and height
+     * @param stream the stream of image data
+     * @param bitsPerPixel the number of bits used to store each pixel
+     * @param format the format of the bmp file
+     * @param masks optional color masks for certain bmp formats, passes
+                    ownership to SkBmpCodec
+     * @param colorTable array representing the color table for index-based bmp
+     *                   formats, colors are unpremultiplied, passes ownership
+     *                   to SkBmpCodec
+     * @param rowOrder indicates whether rows are ordered top-down or bottom-up
+     * @param remainingBytes used only for RLE decodes, as we must decode all
+     *                  of the data at once rather than row by row
+     *                  it indicates the amount of data left in the stream
+     *                  after decoding the headers
+     *
+     */
+    SkBmpCodec(const SkImageInfo& srcInfo, SkStream* stream,
+               uint16_t bitsPerPixel, BitmapInputFormat format,
+               SkMasks* masks, SkPMColor* colorTable,
+               RowOrder rowOrder, uint32_t remainingBytes);
+
+    // Fields
+    const uint16_t                      fBitsPerPixel;
+    const BitmapInputFormat             fInputFormat;
+    SkAutoTDelete<SkMasks>              fMasks;          // owned
+    const SkAutoTDeleteArray<SkPMColor> fColorTable;     // owned, unpremul
+    const RowOrder                      fRowOrder;
+    const uint32_t                      fRemainingBytes;
+
+    typedef SkCodec INHERITED;
+};
index 8e7ee33a9554bce341cf8681b2db86d334bfae1b..f42af38fea707787f0fc760dba74d5486acf0572 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "SkCodec_libpng.h"
+#include "SkCodecPriv.h"
 #include "SkColorPriv.h"
 #include "SkColorTable.h"
 #include "SkBitmap.h"
@@ -346,10 +347,6 @@ SkPngCodec::~SkPngCodec() {
 // 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()) {
@@ -466,7 +463,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
         // Now swizzle it.
         uint8_t* row = base;
         for (int y = 0; y < height; y++) {
-            reallyHasAlpha |= swizzler->next(row);
+            reallyHasAlpha |= !SkSwizzler::IsOpaque(swizzler->next(row));
             row += rowBytes;
         }
     } else {
@@ -474,7 +471,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
         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);
+            reallyHasAlpha |= !SkSwizzler::IsOpaque(swizzler->next(srcRow));
         }
     }
 
diff --git a/src/codec/SkMaskSwizzler.cpp b/src/codec/SkMaskSwizzler.cpp
new file mode 100644 (file)
index 0000000..8d9f6c6
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * 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 "SkCodecPriv.h"
+#include "SkColorPriv.h"
+#include "SkMaskSwizzler.h"
+
+/*
+ *
+ * Row procedure for masked color components with 16 bits per pixel
+ *
+ */
+static SkSwizzler::ResultAlpha swizzle_mask16_to_n32(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    uint16_t* srcPtr = (uint16_t*) srcRow;
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    for (int i = 0; i < width; i++) {
+        uint16_t p = srcPtr[i];
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        dstPtr[i] = SkPackARGB32NoCheck(0xFF, red, green, blue);
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+/*
+ *
+ * Row procedure for masked color components with 16 bits per pixel with alpha
+ *
+ */
+static SkSwizzler::ResultAlpha swizzle_mask16_alpha_to_n32(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    uint16_t* srcPtr = (uint16_t*) srcRow;
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    INIT_RESULT_ALPHA;
+    for (int i = 0; i < width; i++) {
+        uint16_t p = srcPtr[i];
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        uint8_t alpha = masks->getAlpha(p);
+        UPDATE_RESULT_ALPHA(alpha);
+        dstPtr[i] = SkPackARGB32NoCheck(alpha, red, green, blue);
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+/*
+ *
+ * Row procedure for masked color components with 24 bits per pixel
+ *
+ */
+static SkSwizzler::ResultAlpha swizzle_mask24_to_n32(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    for (int i = 0; i < 3*width; i += 3) {
+        uint32_t p = srcRow[i] | (srcRow[i + 1] << 8) | srcRow[i + 2] << 16;
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        dstPtr[i/3] = SkPackARGB32NoCheck(0xFF, red, green, blue);
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+/*
+ *
+ * Row procedure for masked color components with 24 bits per pixel with alpha
+ *
+ */
+static SkSwizzler::ResultAlpha swizzle_mask24_alpha_to_n32(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    INIT_RESULT_ALPHA;
+    for (int i = 0; i < 3*width; i += 3) {
+        uint32_t p = srcRow[i] | (srcRow[i + 1] << 8) | srcRow[i + 2] << 16;
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        uint8_t alpha = masks->getAlpha(p);
+        UPDATE_RESULT_ALPHA(alpha);
+        dstPtr[i/3] = SkPackARGB32NoCheck(alpha, red, green, blue);
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+/*
+ *
+ * Row procedure for masked color components with 32 bits per pixel
+ *
+ */
+static SkSwizzler::ResultAlpha swizzle_mask32_to_n32(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    uint32_t* srcPtr = (uint32_t*) srcRow;
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    for (int i = 0; i < width; i++) {
+        uint32_t p = srcPtr[i];
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        dstPtr[i] = SkPackARGB32NoCheck(0xFF, red, green, blue);
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+/*
+ *
+ * Row procedure for masked color components with 32 bits per pixel
+ *
+ */
+static SkSwizzler::ResultAlpha swizzle_mask32_alpha_to_n32(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    uint32_t* srcPtr = (uint32_t*) srcRow;
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    INIT_RESULT_ALPHA;
+    for (int i = 0; i < width; i++) {
+        uint32_t p = srcPtr[i];
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        uint8_t alpha = masks->getAlpha(p);
+        UPDATE_RESULT_ALPHA(alpha);
+        dstPtr[i] = SkPackARGB32NoCheck(alpha, red, green, blue);
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+/*
+ *
+ * Create a new mask swizzler
+ *
+ */
+SkMaskSwizzler* SkMaskSwizzler::CreateMaskSwizzler(
+        const SkImageInfo& imageInfo, SkMasks* masks, uint32_t bitsPerPixel) {
+
+    // Choose the appropriate row procedure
+    RowProc proc = NULL;
+    uint32_t alphaMask = masks->getAlphaMask();
+    switch (bitsPerPixel) {
+        case 16:
+            if (0 == alphaMask) {
+                proc = &swizzle_mask16_to_n32;
+            } else {
+                proc = &swizzle_mask16_alpha_to_n32;
+            }
+            break;
+        case 24:
+            if (0 == alphaMask) {
+                proc = &swizzle_mask24_to_n32;
+            } else {
+                proc = &swizzle_mask24_alpha_to_n32;
+            }
+            break;
+        case 32:
+            if (0 == alphaMask) {
+                proc = &swizzle_mask32_to_n32;
+            } else {
+                proc = &swizzle_mask32_alpha_to_n32;
+            }
+            break;
+        default:
+            SkASSERT(false);
+            return NULL;
+    }
+    return SkNEW_ARGS(SkMaskSwizzler, (imageInfo, masks, proc));
+}
+
+/*
+ *
+ * Constructor for mask swizzler
+ *
+ */
+SkMaskSwizzler::SkMaskSwizzler(const SkImageInfo& imageInfo,
+                               SkMasks* masks, RowProc proc)
+    : fImageInfo(imageInfo)
+    , fMasks(masks)
+    , fRowProc(proc)
+{}
+
+/*
+ *
+ * Swizzle the next row
+ *
+ */
+SkSwizzler::ResultAlpha SkMaskSwizzler::next(void* dst,
+                                             const uint8_t* src) {
+    return fRowProc(dst, src, fImageInfo.width(), fMasks);
+}
diff --git a/src/codec/SkMaskSwizzler.h b/src/codec/SkMaskSwizzler.h
new file mode 100644 (file)
index 0000000..9351f02
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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 "SkMasks.h"
+#include "SkSwizzler.h"
+#include "SkTypes.h"
+
+/*
+ *
+ * Used to swizzle images whose pixel components are extracted by bit masks
+ * Currently only used by bmp
+ *
+ */
+class SkMaskSwizzler {
+public:
+
+    /*
+     *
+     * Create a new swizzler
+     * @param masks Unowned pointer to helper class
+     *
+     */
+    static SkMaskSwizzler* CreateMaskSwizzler(const SkImageInfo& imageInfo,
+                                              SkMasks* masks,
+                                              uint32_t bitsPerPixel);
+
+    /*
+     *
+     * Swizzle the next row
+     *
+     */
+    SkSwizzler::ResultAlpha next(void* dst, const uint8_t* src);
+
+private:
+
+    /*
+     *
+     * Row procedure used for swizzle
+     *
+     */
+    typedef SkSwizzler::ResultAlpha (*RowProc)(
+            void* dstRow, const uint8_t* srcRow, int width,
+            SkMasks* masks);
+
+    /*
+     *
+     * Constructor for mask swizzler
+     *
+     */
+    SkMaskSwizzler(const SkImageInfo& info, SkMasks* masks, RowProc proc);
+
+    // Fields
+    const SkImageInfo& fImageInfo;
+    SkMasks*           fMasks;     // unowned
+    const RowProc      fRowProc;
+};
diff --git a/src/codec/SkMasks.cpp b/src/codec/SkMasks.cpp
new file mode 100644 (file)
index 0000000..1c542ce
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * 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 "SkMasks.h"
+#include "SkTypes.h"
+
+/*
+ *
+ * Used to convert 1-7 bit color components into 8-bit color components
+ *
+ */
+const static uint8_t n_bit_to_8_bit_lookup_table[] = {
+    // 1 bit
+    0, 255,
+    // 2 bits
+    0, 85, 170, 255,
+    // 3 bits
+    0, 36, 73, 109, 146, 182, 219, 255,
+    // 4 bits
+    0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255,
+    // 5 bits
+    0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140,
+    148, 156, 165, 173, 181, 189, 197, 206, 214, 222, 230, 239, 247, 255,
+    // 6 bits
+    0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 45, 49, 53, 57, 61, 65, 69, 73,
+    77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 130, 134, 138,
+    142, 146, 150, 154, 158, 162, 166, 170, 174, 178, 182, 186, 190, 194, 198,
+    202, 206, 210, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 255,
+    // 7 bits
+    0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38,
+    40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76,
+    78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110,
+    112, 114, 116, 118, 120, 122, 124, 126, 129, 131, 133, 135, 137, 139, 141,
+    143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171,
+    173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201,
+    203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231,
+    233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255
+};
+
+/*
+ *
+ * Convert an n bit component to an 8-bit component
+ *
+ */
+static uint8_t convert_to_8(uint32_t component, uint32_t n) {
+    if (0 == n) {
+        return 0;
+    } else if (8 > n) {
+        return n_bit_to_8_bit_lookup_table[(1 << n) - 2 + component];
+    } else {
+        SkASSERT(8 == n);
+        return component;
+    }
+}
+
+static uint8_t get_comp(uint32_t pixel, uint32_t mask, uint32_t shift,
+                        uint32_t size) {
+    return convert_to_8((pixel & mask) >> shift, size);
+}
+
+/*
+ *
+ * Get a color component
+ *
+ */
+uint8_t SkMasks::getRed(uint32_t pixel) {
+    return get_comp(pixel, fRed.mask, fRed.shift, fRed.size);
+}
+uint8_t SkMasks::getGreen(uint32_t pixel) {
+    return get_comp(pixel, fGreen.mask, fGreen.shift, fGreen.size);
+}
+uint8_t SkMasks::getBlue(uint32_t pixel) {
+    return get_comp(pixel, fBlue.mask, fBlue.shift, fBlue.size);
+}
+uint8_t SkMasks::getAlpha(uint32_t pixel) {
+    return get_comp(pixel, fAlpha.mask, fAlpha.shift, fAlpha.size);
+}
+
+/*
+ *
+ * Process an input mask to obtain the necessary information
+ *
+ */
+const SkMasks::MaskInfo process_mask(uint32_t mask, uint32_t bpp) {
+    // Trim the masks to the allowed number of bits
+    if (bpp < 32) {
+        mask &= (1 << bpp) - 1;
+    }
+
+    // Determine properties of the mask
+    uint32_t tempMask = mask;
+    uint32_t shift = 0;
+    uint32_t size = 0;
+    if (tempMask != 0) {
+        // Count trailing zeros on masks
+        for (; (tempMask & 1) == 0; tempMask >>= 1) {
+            shift++;
+        }
+        // Count the size of the mask
+        for (; tempMask & 1; tempMask >>= 1) {
+            size++;
+        }
+        // Check that the mask is continuous
+        if (tempMask != 0) {
+            SkDebugf("Warning: Bit masks is not continuous.\n");
+        }
+        // Truncate masks greater than 8 bits
+        if (size > 8) {
+            shift += size - 8;
+            size = 8;
+        }
+    }
+
+    // Save the calculated values
+    const SkMasks::MaskInfo info = { mask, shift, size };
+    return info;
+}
+
+/*
+ *
+ * Create the masks object
+ *
+ */
+SkMasks* SkMasks::CreateMasks(InputMasks masks, uint32_t bitsPerPixel) {
+    // Trim the input masks according to bitsPerPixel
+    if (bitsPerPixel < 32) {
+        masks.red &= (1 << bitsPerPixel) - 1;
+        masks.green &= (1 << bitsPerPixel) - 1;
+        masks.blue &= (1 << bitsPerPixel) - 1;
+        masks.alpha &= (1 << bitsPerPixel) - 1;
+    }
+
+    // Check that masks do not overlap
+    if (((masks.red & masks.green) | (masks.red & masks.blue) |
+            (masks.red & masks.alpha) | (masks.green & masks.blue) |
+            (masks.green & masks.alpha) | (masks.blue & masks.alpha)) != 0) {
+        return NULL;
+    }
+
+    // Collect information about the masks
+    const MaskInfo red = process_mask(masks.red, bitsPerPixel);
+    const MaskInfo green = process_mask(masks.green, bitsPerPixel);
+    const MaskInfo blue = process_mask(masks.blue, bitsPerPixel);
+    const MaskInfo alpha = process_mask(masks.alpha, bitsPerPixel);
+
+    return SkNEW_ARGS(SkMasks, (red, green, blue, alpha));
+}
+
+
+SkMasks::SkMasks(const MaskInfo red, const MaskInfo green,
+                 const MaskInfo blue, const MaskInfo alpha)
+    : fRed(red)
+    , fGreen(green)
+    , fBlue(blue)
+    , fAlpha(alpha)
+{}
diff --git a/src/codec/SkMasks.h b/src/codec/SkMasks.h
new file mode 100644 (file)
index 0000000..31c6849
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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 "SkTypes.h"
+
+/*
+ *
+ * Contains useful mask routines for SkMaskSwizzler
+ *
+ */
+class SkMasks {
+public:
+
+    /*
+     *
+     * Input bit masks format
+     *
+     */
+    struct InputMasks {
+        uint32_t red;
+        uint32_t green;
+        uint32_t blue;
+        uint32_t alpha;
+    };
+
+    /*
+     *
+     * Contains all of the information for a single mask
+     *
+     */
+     struct MaskInfo {
+        uint32_t mask;
+        uint32_t shift;
+        uint32_t size;
+     };
+
+    /*
+     *
+     * Create the masks object
+     *
+     */
+    static SkMasks* CreateMasks(InputMasks masks, uint32_t bpp);
+
+    /*
+     *
+     * Get a color component
+     *
+     */
+    uint8_t getRed(uint32_t pixel);
+    uint8_t getGreen(uint32_t pixel);
+    uint8_t getBlue(uint32_t pixel);
+    uint8_t getAlpha(uint32_t pixel);
+
+    /*
+     *
+     * Getter for the alpha mask
+     * The alpha mask may be used in other decoding modes
+     *
+     */
+     uint32_t getAlphaMask() {
+        return fAlpha.mask;
+     }
+
+private:
+
+    /*
+     *
+     * Constrcutor
+     *
+     */
+    SkMasks(const MaskInfo red, const MaskInfo green, const MaskInfo blue,
+            const MaskInfo alpha);
+
+    const MaskInfo fRed;
+    const MaskInfo fGreen;
+    const MaskInfo fBlue;
+    const MaskInfo fAlpha;
+};
index 563933f13a03c16e880c2b8132c7df3657fc4868..0668db6db9e45a25091189fa0dff0946f1c64df4 100644 (file)
  * found in the LICENSE file.
  */
 
+#include "SkCodecPriv.h"
 #include "SkColorPriv.h"
 #include "SkSwizzler.h"
 #include "SkTemplates.h"
 
-// index
+SkSwizzler::ResultAlpha SkSwizzler::GetResult(uint8_t zeroAlpha,
+                                              uint8_t maxAlpha) {
+    // In the transparent case, this returns 0x0000
+    // In the opaque case, this returns 0xFFFF
+    // If the row is neither transparent nor opaque, returns something else
+    return (((uint16_t) maxAlpha) << 8) | zeroAlpha;
+}
+
+// kIndex1, kIndex2, kIndex4
+
+static SkSwizzler::ResultAlpha swizzle_small_index_to_n32(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bitsPerPixel, int y, const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*) dstRow;
+    INIT_RESULT_ALPHA;
+    const uint32_t pixelsPerByte = 8 / bitsPerPixel;
+    const size_t rowBytes = compute_row_bytes_ppb(width, pixelsPerByte);
+    const uint8_t mask = (1 << bitsPerPixel) - 1;
+    int x = 0;
+    for (uint32_t byte = 0; byte < rowBytes; byte++) {
+        uint8_t pixelData = src[byte];
+        for (uint32_t p = 0; p < pixelsPerByte && x < width; p++) {
+            uint8_t index = (pixelData >> (8 - bitsPerPixel)) & mask;
+            SkPMColor c = ctable[index];
+            UPDATE_RESULT_ALPHA(c >> SK_A32_SHIFT);
+            dst[x] = c;
+            pixelData <<= bitsPerPixel;
+            x++;
+        }
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
 
-#define A32_MASK_IN_PLACE   (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT)
+// kIndex
 
-static bool swizzle_index_to_n32(void* SK_RESTRICT dstRow,
-                                 const uint8_t* SK_RESTRICT src,
-                                 int width, int deltaSrc, int, const SkPMColor ctable[]) {
+static SkSwizzler::ResultAlpha swizzle_index_to_n32(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
 
     SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
-    SkPMColor cc = A32_MASK_IN_PLACE;
+    INIT_RESULT_ALPHA;
     for (int x = 0; x < width; x++) {
         SkPMColor c = ctable[*src];
-        cc &= c;
+        UPDATE_RESULT_ALPHA(c >> SK_A32_SHIFT);
         dst[x] = c;
-        src += deltaSrc;
+        src++;
     }
-    return cc != A32_MASK_IN_PLACE;
+    return COMPUTE_RESULT_ALPHA;
 }
 
-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[]) {
+static SkSwizzler::ResultAlpha swizzle_index_to_n32_skipZ(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
 
     SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
-    SkPMColor cc = A32_MASK_IN_PLACE;
+    INIT_RESULT_ALPHA;
     for (int x = 0; x < width; x++) {
         SkPMColor c = ctable[*src];
-        cc &= c;
+        UPDATE_RESULT_ALPHA(c >> SK_A32_SHIFT);
         if (c != 0) {
             dst[x] = c;
         }
-        src += deltaSrc;
+        src++;
     }
-    return cc != A32_MASK_IN_PLACE;
+    return COMPUTE_RESULT_ALPHA;
 }
 
 #undef A32_MASK_IN_PLACE
 
+static SkSwizzler::ResultAlpha swizzle_bgrx_to_n32(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPackARGB32NoCheck(0xFF, src[2], src[1], src[0]);
+        src += bytesPerPixel;
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+// kBGRA
+
+static SkSwizzler::ResultAlpha swizzle_bgra_to_n32(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    INIT_RESULT_ALPHA;
+    for (int x = 0; x < width; x++) {
+        uint8_t alpha = src[3];
+        UPDATE_RESULT_ALPHA(alpha);
+        dst[x] = SkPackARGB32NoCheck(alpha, src[2], src[1], src[0]);
+        src += bytesPerPixel;
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
 // n32
-static bool swizzle_rgbx_to_n32(void* SK_RESTRICT dstRow,
-                                const uint8_t* SK_RESTRICT src,
-                                int width, int deltaSrc, int, const SkPMColor[]) {
+static SkSwizzler::ResultAlpha swizzle_rgbx_to_n32(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
     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;
+        src += bytesPerPixel;
     }
-    return false;
+    return SkSwizzler::kOpaque_ResultAlpha;
 }
 
-static bool swizzle_rgba_to_n32_premul(void* SK_RESTRICT dstRow,
-                                       const uint8_t* SK_RESTRICT src,
-                                       int width, int deltaSrc, int, const SkPMColor[]) {
+static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_premul(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
     SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
-    unsigned alphaMask = 0xFF;
+    INIT_RESULT_ALPHA;
     for (int x = 0; x < width; x++) {
         unsigned alpha = src[3];
+        UPDATE_RESULT_ALPHA(alpha);
         dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
-        src += deltaSrc;
-        alphaMask &= alpha;
+        src += bytesPerPixel;
     }
-    return alphaMask != 0xFF;
+    return COMPUTE_RESULT_ALPHA;
 }
 
-static bool swizzle_rgba_to_n32_unpremul(void* SK_RESTRICT dstRow,
-                                         const uint8_t* SK_RESTRICT src,
-                                         int width, int deltaSrc, int,
-                                         const SkPMColor[]) {
+static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_unpremul(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
     uint32_t* SK_RESTRICT dst = reinterpret_cast<uint32_t*>(dstRow);
-    unsigned alphaMask = 0xFF;
+    INIT_RESULT_ALPHA;
     for (int x = 0; x < width; x++) {
         unsigned alpha = src[3];
+        UPDATE_RESULT_ALPHA(alpha);
         dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]);
-        src += deltaSrc;
-        alphaMask &= alpha;
+        src += bytesPerPixel;
     }
-    return alphaMask != 0xFF;
+    return COMPUTE_RESULT_ALPHA;
 }
 
-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[]) {
+static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_premul_skipZ(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
     SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
-    unsigned alphaMask = 0xFF;
+    INIT_RESULT_ALPHA;
     for (int x = 0; x < width; x++) {
         unsigned alpha = src[3];
+        UPDATE_RESULT_ALPHA(alpha);
         if (0 != alpha) {
             dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
         }
-        src += deltaSrc;
-        alphaMask &= alpha;
+        src += bytesPerPixel;
     }
-    return alphaMask != 0xFF;
+    return COMPUTE_RESULT_ALPHA;
 }
 
 /**
@@ -114,7 +177,7 @@ static bool swizzle_rgba_to_n32_premul_skipZ(void* SK_RESTRICT dstRow,
     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,
+                                               int width, int bitsPerPixel,
                                                const SkPMColor[]) {
     SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
     unsigned alphaMask = 0xFF;
@@ -133,31 +196,61 @@ static bool swizzle_rgba_to_n32_unpremul_skipZ(void* SK_RESTRICT dstRow,
 }
 */
 
-SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, const SkPMColor* ctable,
+SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc,
+                                       const SkPMColor* ctable,
                                        const SkImageInfo& info, void* dst,
                                        size_t dstRowBytes, bool skipZeroes) {
-    if (info.colorType() == kUnknown_SkColorType) {
+    if (kUnknown_SkColorType == info.colorType()) {
         return NULL;
     }
     if (info.minRowBytes() > dstRowBytes) {
         return  NULL;
     }
-    if (kIndex == sc && NULL == ctable) {
+    if ((kIndex == sc || kIndex4 == sc || kIndex2 == sc || kIndex1 == sc)
+            && NULL == ctable) {
         return NULL;
     }
     RowProc proc = NULL;
     switch (sc) {
+        case kIndex1:
+        case kIndex2:
+        case kIndex4:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    proc = &swizzle_small_index_to_n32;
+                    break;
+                default:
+                    break;
+            }
+            break;
         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 kBGR:
+        case kBGRX:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    proc = &swizzle_bgrx_to_n32;
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case kBGRA:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    proc = &swizzle_bgra_to_n32;
+                    break;
                 default:
                     break;
             }
@@ -190,32 +283,67 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, const SkPMColor
                     break;
             }
             break;
+        case kRGB:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    proc = &swizzle_rgbx_to_n32;
+                    break;
+                default:
+                    break;
+            }
+            break;
         default:
             break;
     }
     if (NULL == proc) {
         return NULL;
     }
-    return SkNEW_ARGS(SkSwizzler, (proc, ctable, BytesPerPixel(sc), info, dst, dstRowBytes));
+
+    // Store deltaSrc in bytes if it is an even multiple, otherwise use bits
+    int deltaSrc = SkIsAlign8(BitsPerPixel(sc)) ? BytesPerPixel(sc) :
+            BitsPerPixel(sc);
+    return SkNEW_ARGS(SkSwizzler, (proc, ctable, deltaSrc, info, dst,
+                                   dstRowBytes));
 }
 
-SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcBpp,
-                       const SkImageInfo& info, void* dst, size_t rowBytes)
+SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable,
+                       int deltaSrc, const SkImageInfo& info, void* dst,
+                       size_t rowBytes)
     : fRowProc(proc)
     , fColorTable(ctable)
-    , fSrcPixelSize(srcBpp)
+    , fDeltaSrc(deltaSrc)
     , fDstInfo(info)
     , fDstRow(dst)
     , fDstRowBytes(rowBytes)
     , fCurrY(0)
 {
+    SkDEBUGCODE(fNextMode = kUninitialized_NextMode);
 }
 
-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++;
+SkSwizzler::ResultAlpha SkSwizzler::next(const uint8_t* SK_RESTRICT src) {
+    SkASSERT(0 <= fCurrY && fCurrY < fDstInfo.height());
+    SkASSERT(kDesignateRow_NextMode != fNextMode);
+    SkDEBUGCODE(fNextMode = kConsecutive_NextMode);
+
+    // Decode a row
+    const ResultAlpha result = fRowProc(fDstRow, src, fDstInfo.width(),
+            fDeltaSrc, fCurrY, fColorTable);
+
+    // Move to the next row and return the result
     fDstRow = SkTAddOffset<void>(fDstRow, fDstRowBytes);
-    return hadAlpha;
+    return result;
+}
+
+SkSwizzler::ResultAlpha SkSwizzler::next(const uint8_t* SK_RESTRICT src,
+        int y) {
+    SkASSERT(0 <= y && y < fDstInfo.height());
+    SkASSERT(kConsecutive_NextMode != fNextMode);
+    SkDEBUGCODE(fNextMode = kDesignateRow_NextMode);
+    
+    // Choose the row
+    void* row = SkTAddOffset<void>(fDstRow, y*fDstRowBytes);
+
+    // Decode the row
+    return fRowProc(row, src, fDstInfo.width(), fDeltaSrc, fCurrY,
+            fColorTable);
 }
index 0bf2ee306a9377c64e5691260b2e9272ea853e06..e22c05b2679cb6c72832ec6506498372578dd2b0 100644 (file)
@@ -18,32 +18,99 @@ 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
+        kGray,
+        kIndex1,
+        kIndex2,
+        kIndex4,
+        kIndex,
+        kRGB,
+        kBGR,
+        kRGBX,
+        kBGRX,
+        kRGBA,
+        kBGRA,
+        kRGB_565,
     };
 
-    static int BytesPerPixel(SrcConfig sc) {
+    /*
+     *
+     * Result code for the alpha components of a row.
+     *
+     */
+    typedef uint16_t ResultAlpha;
+    static const ResultAlpha kOpaque_ResultAlpha = 0xFFFF;
+    static const ResultAlpha kTransparent_ResultAlpha = 0x0000;
+
+    /*
+     *
+     * Checks if the result of decoding a row indicates that the row was
+     * transparent.
+     *
+     */
+    static bool IsTransparent(ResultAlpha r) {
+        return kTransparent_ResultAlpha == r;
+    }
+
+    /*
+     *
+     * Checks if the result of decoding a row indicates that the row was
+     * opaque.
+     *
+     */
+    static bool IsOpaque(ResultAlpha r) {
+        return kOpaque_ResultAlpha == r;
+    }
+    
+    /*
+     *
+     * Constructs the proper result code based on accumulated alpha masks
+     *
+     */
+    static ResultAlpha GetResult(uint8_t zeroAlpha, uint8_t maxAlpha);
+
+    /*
+     *
+     * Returns bits per pixel for source config
+     *
+     */
+    static int BitsPerPixel(SrcConfig sc) {
         switch (sc) {
+            case kIndex1:
+                return 1;
+            case kIndex2:
+                return 2;
+            case kIndex4:
+                return 4;
             case kGray:
             case kIndex:
-                return 1;
+                return 8;
+            case kRGB_565:
+                return 16;
             case kRGB:
-                return 3;
+            case kBGR:
+                return 24;
             case kRGBX:
             case kRGBA:
-                return 4;
-            case kRGB_565:
-                return 2;
+            case kBGRX:
+            case kBGRA:
+                return 32;
             default:
-                SkDebugf("invalid source config passed to BytesPerPixel\n");
-                return -1;
+                SkASSERT(false);
+                return 0;
         }
     }
 
+    /*
+     *
+     * Returns bytes per pixel for source config
+     * Raises an error if each pixel is not stored in an even number of bytes
+     *
+     */
+    static int BytesPerPixel(SrcConfig sc) {
+        SkASSERT(SkIsAlign8(BitsPerPixel(sc)));
+        return BitsPerPixel(sc) >> 3;
+    }
+
     /**
      *  Create a new SkSwizzler.
      *  @param sc SrcConfig
@@ -58,36 +125,68 @@ public:
     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.
+     *  @return A result code describing if the row was fully opaque, fully
+     *          transparent, or neither
      */
-    bool next(const uint8_t* SK_RESTRICT src);
+    ResultAlpha next(const uint8_t* SK_RESTRICT src);
+
+    /**
+     *
+     * Alternate version of next that allows the caller to specify the row.
+     * It is very important to only use one version of next.  Since the other
+     * version modifies the dst pointer, it will change the behavior of this
+     * function.  We will check this in Debug mode.
+     *
+     */
+    ResultAlpha next(const uint8_t* SK_RESTRICT src, int y);
 private:
+
+#ifdef SK_DEBUG
+    /*
+     *
+     * Keep track of which version of next the caller is using
+     *
+     */
+    enum NextMode {
+        kUninitialized_NextMode,
+        kConsecutive_NextMode,
+        kDesignateRow_NextMode,
+    };
+
+    NextMode fNextMode;
+#endif
+
     /**
      *  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 deltaSrc if bitsPerPixel % 8 == 0, deltaSrc is bytesPerPixel
+     *                  else, deltaSrc is bitsPerPixel
      *  @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[]);
+    typedef ResultAlpha (*RowProc)(void* SK_RESTRICT dstRow,
+                                   const uint8_t* SK_RESTRICT src,
+                                   int width, int deltaSrc, int y,
+                                   const SkPMColor ctable[]);
 
     const RowProc       fRowProc;
-    const SkPMColor*    fColorTable;    // Unowned pointer
-    const int           fSrcPixelSize;
+    const SkPMColor*    fColorTable;      // Unowned pointer
+    const int           fDeltaSrc;        // if bitsPerPixel % 8 == 0
+                                          //     deltaSrc is bytesPerPixel
+                                          // else
+                                          //     deltaSrc is bitsPerPixel
     const SkImageInfo   fDstInfo;
     void*               fDstRow;
     const size_t        fDstRowBytes;
     int                 fCurrY;
 
-    SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcBpp,
+    SkSwizzler(RowProc proc, const SkPMColor* ctable, int deltaSrc,
                const SkImageInfo& info, void* dst, size_t rowBytes);
 
 };