Split SkBmpCodec into three separate classes
Will regress behavior on gold on test32bfv4.bmp, where we
will no longer fix transparent decodes.
TODO: Start fixing transparent decodes again, or decide
that we don't want to fix them and remove isTransparent
from SkSwizzler. I think this may become more clear when I
start implementing the scanline decoder.
BUG=skia:
Review URL: https://codereview.chromium.org/1258863008
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp
index a1f1569..4383382 100644
--- a/src/codec/SkBmpCodec.cpp
+++ b/src/codec/SkBmpCodec.cpp
@@ -6,93 +6,55 @@
*/
#include "SkBmpCodec.h"
+#include "SkBmpMaskCodec.h"
+#include "SkBmpRLECodec.h"
+#include "SkBmpStandardCodec.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) {
- // Ensure that the profile type is unchanged
- if (dst.profileType() != src.profileType()) {
- return false;
- }
-
- // Check for supported alpha types
- if (src.alphaType() != dst.alphaType()) {
- if (kOpaque_SkAlphaType == src.alphaType()) {
- // If the source is opaque, we must decode to opaque
- return false;
- }
-
- // The source is not opaque
- switch (dst.alphaType()) {
- case kPremul_SkAlphaType:
- case kUnpremul_SkAlphaType:
- // The source is not opaque, so either of these is okay
- break;
- default:
- // We cannot decode a non-opaque image to opaque (or unknown)
- return false;
- }
- }
-
- // Check for supported color types
- switch (dst.colorType()) {
- // Allow output to kN32 from any type of input
- case kN32_SkColorType:
- return true;
- // Allow output to kIndex_8 from compatible inputs
- case kIndex_8_SkColorType:
- return kIndex_8_SkColorType == src.colorType();
- default:
- 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
+enum BmpHeaderType {
+ kInfoV1_BmpHeaderType,
+ kInfoV2_BmpHeaderType,
+ kInfoV3_BmpHeaderType,
+ kInfoV4_BmpHeaderType,
+ kInfoV5_BmpHeaderType,
+ kOS2V1_BmpHeaderType,
+ kOS2VX_BmpHeaderType,
+ kUnknown_BmpHeaderType
};
/*
- *
* 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
+enum BmpCompressionMethod {
+ kNone_BmpCompressionMethod = 0,
+ k8BitRLE_BmpCompressionMethod = 1,
+ k4BitRLE_BmpCompressionMethod = 2,
+ kBitMasks_BmpCompressionMethod = 3,
+ kJpeg_BmpCompressionMethod = 4,
+ kPng_BmpCompressionMethod = 5,
+ kAlphaBitMasks_BmpCompressionMethod = 6,
+ kCMYK_BmpCompressionMethod = 11,
+ kCMYK8BitRLE_BmpCompressionMethod = 12,
+ kCMYK4BitRLE_BmpCompressionMethod = 13
};
/*
- *
+ * Used to define the input format of the bmp
+ */
+enum BmpInputFormat {
+ kStandard_BmpInputFormat,
+ kRLE_BmpInputFormat,
+ kBitMask_BmpInputFormat,
+ kUnknown_BmpInputFormat
+};
+
+/*
* 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"
@@ -103,35 +65,29 @@
}
/*
- *
* Assumes IsBmp was called and returned true
* Creates a bmp decoder
* Reads enough of the stream to determine the image format
- *
*/
SkCodec* SkBmpCodec::NewFromStream(SkStream* stream) {
return SkBmpCodec::NewFromStream(stream, false);
}
/*
- *
* Creates a bmp decoder for a bmp embedded in ico
* Reads enough of the stream to determine the image format
- *
*/
SkCodec* SkBmpCodec::NewFromIco(SkStream* stream) {
return SkBmpCodec::NewFromStream(stream, true);
}
/*
- *
* Read enough of the stream to initialize the SkBmpCodec. Returns a bool
* representing success or failure. If it returned true, and codecOut was
* not NULL, it will be set to a new SkBmpCodec.
* Does *not* take ownership of the passed in SkStream.
- *
*/
-bool SkBmpCodec::ReadHeader(SkStream* stream, bool isIco, SkCodec** codecOut) {
+bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) {
// Header size constants
static const uint32_t kBmpHeaderBytes = 14;
static const uint32_t kBmpHeaderBytesPlusFour = kBmpHeaderBytes + 4;
@@ -155,7 +111,7 @@
uint32_t infoBytes;
// Bmps embedded in Icos skip the first Bmp header
- if (!isIco) {
+ if (!inIco) {
// Read the first header and the size of the second header
SkAutoTDeleteArray<uint8_t> hBuffer(
SkNEW_ARRAY(uint8_t, kBmpHeaderBytesPlusFour));
@@ -220,7 +176,7 @@
uint16_t bitsPerPixel;
// The compression method for the pixel data
- uint32_t compression = kNone_BitmapCompressionMethod;
+ uint32_t compression = kNone_BmpCompressionMethod;
// Number of colors in the color table, defaults to 0 or max (see below)
uint32_t numColors = 0;
@@ -232,24 +188,24 @@
int width, height;
// Determine image information depending on second header format
- BitmapHeaderType headerType;
+ BmpHeaderType headerType;
if (infoBytes >= kBmpInfoBaseBytes) {
// Check the version of the header
switch (infoBytes) {
case kBmpInfoV1Bytes:
- headerType = kInfoV1_BitmapHeaderType;
+ headerType = kInfoV1_BmpHeaderType;
break;
case kBmpInfoV2Bytes:
- headerType = kInfoV2_BitmapHeaderType;
+ headerType = kInfoV2_BmpHeaderType;
break;
case kBmpInfoV3Bytes:
- headerType = kInfoV3_BitmapHeaderType;
+ headerType = kInfoV3_BmpHeaderType;
break;
case kBmpInfoV4Bytes:
- headerType = kInfoV4_BitmapHeaderType;
+ headerType = kInfoV4_BmpHeaderType;
break;
case kBmpInfoV5Bytes:
- headerType = kInfoV5_BitmapHeaderType;
+ headerType = kInfoV5_BmpHeaderType;
break;
case 16:
case 20:
@@ -262,7 +218,7 @@
case 48:
case 60:
case kBmpOS2V2Bytes:
- headerType = kOS2VX_BitmapHeaderType;
+ headerType = kOS2VX_BmpHeaderType;
break;
default:
// We do not signal an error here because there is the
@@ -271,7 +227,7 @@
// build off of the older versions, so we may still be able to
// decode the bmp.
SkCodecPrintf("Warning: unknown bmp header format.\n");
- headerType = kUnknown_BitmapHeaderType;
+ headerType = kUnknown_BmpHeaderType;
break;
}
// We check the size of the header before entering the if statement.
@@ -296,7 +252,7 @@
bytesPerColor = 4;
} else if (infoBytes >= kBmpOS2V1Bytes) {
// The OS2V1 is treated separately because it has a unique format
- headerType = kOS2V1_BitmapHeaderType;
+ headerType = kOS2V1_BmpHeaderType;
width = (int) get_short(iBuffer.get(), 0);
height = (int) get_short(iBuffer.get(), 2);
bitsPerPixel = get_short(iBuffer.get(), 6);
@@ -315,7 +271,7 @@
}
// The height field for bmp in ico is double the actual height because they
// contain an XOR mask followed by an AND mask
- if (isIco) {
+ if (inIco) {
height /= 2;
}
if (width <= 0 || height <= 0) {
@@ -331,31 +287,31 @@
// Determine the input compression format and set bit masks if necessary
uint32_t maskBytes = 0;
- BitmapInputFormat inputFormat = kUnknown_BitmapInputFormat;
+ BmpInputFormat inputFormat = kUnknown_BmpInputFormat;
switch (compression) {
- case kNone_BitmapCompressionMethod:
- inputFormat = kStandard_BitmapInputFormat;
+ case kNone_BmpCompressionMethod:
+ inputFormat = kStandard_BmpInputFormat;
break;
- case k8BitRLE_BitmapCompressionMethod:
+ case k8BitRLE_BmpCompressionMethod:
if (bitsPerPixel != 8) {
SkCodecPrintf("Warning: correcting invalid bitmap format.\n");
bitsPerPixel = 8;
}
- inputFormat = kRLE_BitmapInputFormat;
+ inputFormat = kRLE_BmpInputFormat;
break;
- case k4BitRLE_BitmapCompressionMethod:
+ case k4BitRLE_BmpCompressionMethod:
if (bitsPerPixel != 4) {
SkCodecPrintf("Warning: correcting invalid bitmap format.\n");
bitsPerPixel = 4;
}
- inputFormat = kRLE_BitmapInputFormat;
+ inputFormat = kRLE_BmpInputFormat;
break;
- case kAlphaBitMasks_BitmapCompressionMethod:
- case kBitMasks_BitmapCompressionMethod:
+ case kAlphaBitMasks_BmpCompressionMethod:
+ case kBitMasks_BmpCompressionMethod:
// Load the masks
- inputFormat = kBitMask_BitmapInputFormat;
+ inputFormat = kBitMask_BmpInputFormat;
switch (headerType) {
- case kInfoV1_BitmapHeaderType: {
+ case kInfoV1_BmpHeaderType: {
// The V1 header stores the bit masks after the header
SkAutoTDeleteArray<uint8_t> mBuffer(
SkNEW_ARRAY(uint8_t, kBmpMaskBytes));
@@ -370,10 +326,10 @@
inputMasks.blue = get_int(mBuffer.get(), 8);
break;
}
- case kInfoV2_BitmapHeaderType:
- case kInfoV3_BitmapHeaderType:
- case kInfoV4_BitmapHeaderType:
- case kInfoV5_BitmapHeaderType:
+ case kInfoV2_BmpHeaderType:
+ case kInfoV3_BmpHeaderType:
+ case kInfoV4_BmpHeaderType:
+ case kInfoV5_BmpHeaderType:
// Header types are matched based on size. If the header
// is V2+, we are guaranteed to be able to read at least
// this size.
@@ -382,7 +338,7 @@
inputMasks.green = get_int(iBuffer.get(), 40);
inputMasks.blue = get_int(iBuffer.get(), 44);
break;
- case kOS2VX_BitmapHeaderType:
+ case kOS2VX_BmpHeaderType:
// 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
@@ -394,21 +350,21 @@
return false;
}
break;
- case kJpeg_BitmapCompressionMethod:
+ case kJpeg_BmpCompressionMethod:
if (24 == bitsPerPixel) {
- inputFormat = kRLE_BitmapInputFormat;
+ inputFormat = kRLE_BmpInputFormat;
break;
}
// Fall through
- case kPng_BitmapCompressionMethod:
+ case kPng_BmpCompressionMethod:
// 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.
SkCodecPrintf("Error: compression format not supported.\n");
return false;
- case kCMYK_BitmapCompressionMethod:
- case kCMYK8BitRLE_BitmapCompressionMethod:
- case kCMYK4BitRLE_BitmapCompressionMethod:
+ case kCMYK_BmpCompressionMethod:
+ case kCMYK8BitRLE_BmpCompressionMethod:
+ case kCMYK4BitRLE_BmpCompressionMethod:
// TODO: Same as above.
SkCodecPrintf("Error: CMYK not supported for bitmap decoding.\n");
return false;
@@ -427,9 +383,9 @@
// out to be fully transparent.
// As an exception, V3 bmp-in-ico may use an alpha mask.
SkAlphaType alphaType = kOpaque_SkAlphaType;
- if ((kInfoV3_BitmapHeaderType == headerType && isIco) ||
- kInfoV4_BitmapHeaderType == headerType ||
- kInfoV5_BitmapHeaderType == headerType) {
+ if ((kInfoV3_BmpHeaderType == headerType && inIco) ||
+ kInfoV4_BmpHeaderType == headerType ||
+ kInfoV5_BmpHeaderType == headerType) {
// Header types are matched based on size. If the header is
// V3+, we are guaranteed to be able to read at least this size.
SkASSERT(infoBytesRemaining > 52);
@@ -443,7 +399,7 @@
// Additionally, 32 bit bmp-in-icos use the alpha channel.
// And, RLE inputs may skip pixels, leaving them as transparent. This
// is uncommon, but we cannot be certain that an RLE bmp will be opaque.
- if ((isIco && 32 == bitsPerPixel) || (kRLE_BitmapInputFormat == inputFormat)) {
+ if ((inIco && 32 == bitsPerPixel) || (kRLE_BmpInputFormat == inputFormat)) {
alphaType = kUnpremul_SkAlphaType;
}
@@ -458,11 +414,11 @@
// 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) {
+ if (kBitMask_BmpInputFormat != inputFormat) {
inputMasks.red = 0x7C00;
inputMasks.green = 0x03E0;
inputMasks.blue = 0x001F;
- inputFormat = kBitMask_BitmapInputFormat;
+ inputFormat = kBitMask_BmpInputFormat;
}
break;
// We want to decode to kIndex_8 for input formats that are already
@@ -474,7 +430,7 @@
// However, we cannot in RLE format since we may need to leave some
// pixels as transparent. Similarly, we also cannot for ICO images
// since we may need to apply a transparent mask.
- if (kRLE_BitmapInputFormat != inputFormat && !isIco) {
+ if (kRLE_BmpInputFormat != inputFormat && !inIco) {
colorType = kIndex_8_SkColorType;
}
case 24:
@@ -494,7 +450,7 @@
}
// Check for a valid number of total bytes when in RLE mode
- if (totalBytes <= offset && kRLE_BitmapInputFormat == inputFormat) {
+ if (totalBytes <= offset && kRLE_BmpInputFormat == inputFormat) {
SkCodecPrintf("Error: RLE requires valid input size.\n");
return false;
}
@@ -502,36 +458,65 @@
// Calculate the number of bytes read so far
const uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes;
- if (!isIco && offset < bytesRead) {
+ if (!inIco && offset < bytesRead) {
SkCodecPrintf("Error: pixel data offset less than header size.\n");
return false;
}
if (codecOut) {
- // Return the codec
- // We will use ImageInfo to store width, height, suggested color type, and
- // suggested alpha type.
+ // Set the image info
const SkImageInfo& imageInfo = SkImageInfo::Make(width, height,
colorType, alphaType);
- *codecOut = SkNEW_ARGS(SkBmpCodec, (imageInfo, stream, bitsPerPixel,
- inputFormat, masks.detach(),
- numColors, bytesPerColor,
- offset - bytesRead, rowOrder,
- RLEBytes, isIco));
+
+ // Return the codec
+ switch (inputFormat) {
+ case kStandard_BmpInputFormat:
+ *codecOut = SkNEW_ARGS(SkBmpStandardCodec, (imageInfo, stream,
+ bitsPerPixel, numColors, bytesPerColor,
+ offset - bytesRead, rowOrder, inIco));
+ return true;
+ case kBitMask_BmpInputFormat:
+ // Bmp-in-Ico must be standard mode
+ if (inIco) {
+ return false;
+ }
+ // Skip to the start of the pixel array.
+ // We can do this here because there is no color table to read
+ // in bit mask mode.
+ if (stream->skip(offset - bytesRead) != offset - bytesRead) {
+ SkCodecPrintf("Error: unable to skip to image data.\n");
+ return false;
+ }
+
+ *codecOut = SkNEW_ARGS(SkBmpMaskCodec, (imageInfo, stream,
+ bitsPerPixel, masks.detach(), rowOrder));
+ return true;
+ case kRLE_BmpInputFormat:
+ // Bmp-in-Ico must be standard mode
+ if (inIco) {
+ return false;
+ }
+ *codecOut = SkNEW_ARGS(SkBmpRLECodec, (
+ imageInfo, stream, bitsPerPixel, numColors,
+ bytesPerColor, offset - bytesRead, rowOrder, RLEBytes));
+ return true;
+ default:
+ SkASSERT(false);
+ return false;
+ }
}
+
return true;
}
/*
- *
* Creates a bmp decoder
* Reads enough of the stream to determine the image format
- *
*/
-SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, bool isIco) {
+SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, bool inIco) {
SkAutoTDelete<SkStream> streamDeleter(stream);
SkCodec* codec = NULL;
- if (ReadHeader(stream, isIco, &codec)) {
+ if (ReadHeader(stream, inIco, &codec)) {
// codec has taken ownership of stream, so we do not need to
// delete it.
SkASSERT(codec);
@@ -541,695 +526,48 @@
return NULL;
}
-/*
- *
- * Creates an instance of the decoder
- * Called only by NewFromStream
- *
- */
SkBmpCodec::SkBmpCodec(const SkImageInfo& info, SkStream* stream,
- uint16_t bitsPerPixel, BitmapInputFormat inputFormat,
- SkMasks* masks, uint32_t numColors,
- uint32_t bytesPerColor, uint32_t offset,
- RowOrder rowOrder, size_t RLEBytes, bool isIco)
+ uint16_t bitsPerPixel, RowOrder rowOrder)
: INHERITED(info, stream)
, fBitsPerPixel(bitsPerPixel)
- , fInputFormat(inputFormat)
- , fMasks(masks)
- , fColorTable(NULL)
- , fNumColors(numColors)
- , fBytesPerColor(bytesPerColor)
- , fOffset(offset)
, fRowOrder(rowOrder)
- , fRLEBytes(RLEBytes)
- , fIsIco(isIco)
-
{}
/*
- *
- * Initiates the bitmap decode
- *
+ * Rewinds the image stream if necessary
*/
-SkCodec::Result SkBmpCodec::onGetPixels(const SkImageInfo& dstInfo,
- void* dst, size_t dstRowBytes,
- const Options& opts,
- SkPMColor* inputColorPtr,
- int* inputColorCount) {
- // Check for proper input and output formats
+bool SkBmpCodec::handleRewind(bool inIco) {
SkCodec::RewindState rewindState = this->rewindIfNeeded();
if (rewindState == kCouldNotRewind_RewindState) {
- return kCouldNotRewind;
+ return false;
} else if (rewindState == kRewound_RewindState) {
- if (!ReadHeader(this->stream(), fIsIco, NULL)) {
- return kCouldNotRewind;
- }
- }
- if (opts.fSubset) {
- // Subsets are not supported.
- return kUnimplemented;
- }
- if (dstInfo.dimensions() != this->getInfo().dimensions()) {
- SkCodecPrintf("Error: scaling not supported.\n");
- return kInvalidScale;
- }
- if (!conversion_possible(dstInfo, this->getInfo())) {
- SkCodecPrintf("Error: cannot convert input type to output type.\n");
- return kInvalidConversion;
- }
-
- // Create the color table if necessary and prepare the stream for decode
- // Note that if it is non-NULL, inputColorCount will be modified
- if (!createColorTable(dstInfo.alphaType(), inputColorCount)) {
- SkCodecPrintf("Error: could not create color table.\n");
- return kInvalidInput;
- }
-
- // Copy the color table to the client if necessary
- copy_color_table(dstInfo, fColorTable, inputColorPtr, inputColorCount);
-
- // Perform the decode
- switch (fInputFormat) {
- case kBitMask_BitmapInputFormat:
- return decodeMask(dstInfo, dst, dstRowBytes, opts);
- case kRLE_BitmapInputFormat:
- return decodeRLE(dstInfo, dst, dstRowBytes, opts);
- case kStandard_BitmapInputFormat:
- return decode(dstInfo, dst, dstRowBytes, opts);
- default:
- SkASSERT(false);
- return kInvalidInput;
- }
-}
-
-/*
- *
- * Process the color table for the bmp input
- *
- */
- bool SkBmpCodec::createColorTable(SkAlphaType alphaType, int* numColors) {
- // Allocate memory for color table
- uint32_t colorBytes = 0;
- uint32_t maxColors = 0;
- SkPMColor colorTable[256];
- if (fBitsPerPixel <= 8) {
- // Zero is a default for maxColors
- // Also set fNumColors to maxColors when it is too large
- maxColors = 1 << fBitsPerPixel;
- if (fNumColors == 0 || fNumColors >= maxColors) {
- fNumColors = maxColors;
- }
-
- // Inform the caller of the number of colors
- if (NULL != numColors) {
- // We set the number of colors to maxColors in order to ensure
- // safe memory accesses. Otherwise, an invalid pixel could
- // access memory outside of our color table array.
- *numColors = maxColors;
- }
-
- // Read the color table from the stream
- colorBytes = fNumColors * fBytesPerColor;
- SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes));
- if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
- SkCodecPrintf("Error: unable to read color table.\n");
- return false;
- }
-
- // Choose the proper packing function
- SkPMColor (*packARGB) (uint32_t, uint32_t, uint32_t, uint32_t);
- switch (alphaType) {
- case kOpaque_SkAlphaType:
- case kUnpremul_SkAlphaType:
- packARGB = &SkPackARGB32NoCheck;
- break;
- case kPremul_SkAlphaType:
- packARGB = &SkPreMultiplyARGB;
- break;
- default:
- // This should not be reached because conversion possible
- // should fail if the alpha type is not one of the above
- // values.
- SkASSERT(false);
- packARGB = NULL;
- break;
- }
-
- // Fill in the color table
- uint32_t i = 0;
- for (; i < fNumColors; i++) {
- uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
- uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
- uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
- uint8_t alpha;
- if (kOpaque_SkAlphaType == alphaType || kRLE_BitmapInputFormat == fInputFormat) {
- alpha = 0xFF;
- } else {
- alpha = (fMasks->getAlphaMask() >> 24) &
- get_byte(cBuffer.get(), i*fBytesPerColor + 3);
- }
- colorTable[i] = packARGB(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);
- }
-
- // Set the color table
- fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorTable, maxColors)));
- }
-
- // Bmp-in-Ico files do not use an offset to indicate where the pixel data
- // begins. Pixel data always begins immediately after the color table.
- if (!fIsIco) {
- // Check that we have not read past the pixel array offset
- if(fOffset < colorBytes) {
- // 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.
- SkCodecPrintf("Error: pixel data offset less than color table size.\n");
- return false;
- }
-
- // After reading the color table, skip to the start of the pixel array
- if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
- SkCodecPrintf("Error: unable to skip to image data.\n");
+ if (!SkBmpCodec::ReadHeader(this->stream(), inIco, NULL)) {
return false;
}
}
-
- // Return true on success
return true;
}
/*
- *
* Get the destination row to start filling from
- * Used to fill the remainder of the image on incomplete input
- *
+ * Used to fill the remainder of the image on incomplete input for bmps
+ * This is tricky since bmps may be kTopDown or kBottomUp. For kTopDown,
+ * we start filling from where we left off, but for kBottomUp we start
+ * filling at the top of the image.
*/
-static inline void* get_dst_start_row(void* dst, size_t dstRowBytes, int32_t y,
- SkBmpCodec::RowOrder rowOrder) {
- return (SkBmpCodec::kTopDown_RowOrder == rowOrder) ?
- SkTAddOffset<void*>(dst, y * dstRowBytes) : dst;
+void* SkBmpCodec::getDstStartRow(void* dst, size_t dstRowBytes, int32_t y) const {
+ return (kTopDown_RowOrder == fRowOrder) ? SkTAddOffset<void*>(dst, y * dstRowBytes) : dst;
}
/*
- *
- * Performs the bitmap decoding for bit masks input format
- *
+ * Compute the number of colors in the color table
*/
-SkCodec::Result SkBmpCodec::decodeMask(const SkImageInfo& dstInfo,
- void* dst, size_t dstRowBytes,
- const Options& opts) {
- // Set constant values
- const int width = dstInfo.width();
- const int height = dstInfo.height();
- const size_t rowBytes = SkAlign4(compute_row_bytes(width, fBitsPerPixel));
-
- // Allocate a buffer large enough to hold the full image
- SkAutoTDeleteArray<uint8_t>
- srcBuffer(SkNEW_ARRAY(uint8_t, height*rowBytes));
- uint8_t* srcRow = srcBuffer.get();
-
- // Create the swizzler
- SkAutoTDelete<SkMaskSwizzler> maskSwizzler(
- 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(srcRow, rowBytes) != rowBytes) {
- SkCodecPrintf("Warning: incomplete input stream.\n");
- // Fill the destination image on failure
- SkPMColor fillColor = dstInfo.alphaType() == kOpaque_SkAlphaType ?
- SK_ColorBLACK : SK_ColorTRANSPARENT;
- if (kNo_ZeroInitialized == opts.fZeroInitialized || 0 != fillColor) {
- void* dstStart = get_dst_start_row(dst, dstRowBytes, y, fRowOrder);
- SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height() - y, fillColor,
- NULL);
- }
- return kIncompleteInput;
- }
-
- // Decode the row in destination format
- int row = kBottomUp_RowOrder == fRowOrder ? height - 1 - y : y;
- void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * row);
- SkSwizzler::ResultAlpha r = maskSwizzler->swizzle(dstRow, srcRow);
- transparent &= SkSwizzler::IsTransparent(r);
-
- // Move to the next row
- srcRow = SkTAddOffset<uint8_t>(srcRow, rowBytes);
+uint32_t SkBmpCodec::computeNumColors(uint32_t numColors) {
+ // Zero is a default for maxColors
+ // Also set fNumColors to maxColors when it is too large
+ uint32_t maxColors = 1 << fBitsPerPixel;
+ if (numColors == 0 || numColors >= maxColors) {
+ return maxColors;
}
-
- // Some fully transparent bmp images are intended to be opaque. Here, we
- // correct for this possibility.
- if (transparent) {
- const SkImageInfo& opaqueInfo =
- dstInfo.makeAlphaType(kOpaque_SkAlphaType);
- SkAutoTDelete<SkMaskSwizzler> opaqueSwizzler(
- SkMaskSwizzler::CreateMaskSwizzler(opaqueInfo, fMasks, fBitsPerPixel));
- srcRow = srcBuffer.get();
- for (int y = 0; y < height; y++) {
- // Decode the row in opaque format
- int row = kBottomUp_RowOrder == fRowOrder ? height - 1 - y : y;
- void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * row);
- opaqueSwizzler->swizzle(dstRow, srcRow);
-
- // Move to the next row
- srcRow = SkTAddOffset<uint8_t>(srcRow, rowBytes);
- }
- }
-
- // Finished decoding the entire image
- return kSuccess;
-}
-
-/*
- *
- * Set an RLE pixel using the color table
- *
- */
-void SkBmpCodec::setRLEPixel(void* dst, size_t dstRowBytes,
- const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
- uint8_t index) {
- // Set the row
- int height = dstInfo.height();
- int row;
- if (kBottomUp_RowOrder == fRowOrder) {
- row = height - y - 1;
- } else {
- row = y;
- }
-
- // Set the pixel based on destination color type
- switch (dstInfo.colorType()) {
- case kN32_SkColorType: {
- SkPMColor* dstRow = SkTAddOffset<SkPMColor>((SkPMColor*) dst,
- row * (int) dstRowBytes);
- dstRow[x] = fColorTable->operator[](index);
- break;
- }
- default:
- // This case should not be reached. We should catch an invalid
- // color type when we check that the conversion is possible.
- SkASSERT(false);
- break;
- }
-}
-
-/*
- *
- * Set an RLE pixel from R, G, B values
- *
- */
-void SkBmpCodec::setRLE24Pixel(void* dst, size_t dstRowBytes,
- const SkImageInfo& dstInfo, uint32_t x,
- uint32_t y, uint8_t red, uint8_t green,
- uint8_t blue) {
- // Set the row
- int height = dstInfo.height();
- int row;
- if (kBottomUp_RowOrder == fRowOrder) {
- row = height - y - 1;
- } else {
- row = y;
- }
-
- // Set the pixel based on destination color type
- switch (dstInfo.colorType()) {
- case kN32_SkColorType: {
- SkPMColor* dstRow = SkTAddOffset<SkPMColor>((SkPMColor*) dst,
- row * (int) dstRowBytes);
- dstRow[x] = SkPackARGB32NoCheck(0xFF, red, green, blue);
- break;
- }
- default:
- // This case should not be reached. We should catch an invalid
- // color type when we check that the conversion is possible.
- SkASSERT(false);
- break;
- }
-}
-
-/*
- *
- * 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,
- const Options& opts) {
- // 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, fRLEBytes));
- size_t totalBytes = stream()->read(buffer.get(), fRLEBytes);
- if (totalBytes < fRLEBytes) {
- SkCodecPrintf("Warning: incomplete RLE file.\n");
- } else if (totalBytes <= 0) {
- SkCodecPrintf("Error: could not read RLE image data.\n");
- return kInvalidInput;
- }
-
- // Destination parameters
- int x = 0;
- int y = 0;
-
- // Set the background as transparent. Then, if the RLE code skips pixels,
- // the skipped pixels will be transparent.
- // Because of the need for transparent pixels, kN32 is the only color
- // type that makes sense for the destination format.
- SkASSERT(kN32_SkColorType == dstInfo.colorType());
- if (kNo_ZeroInitialized == opts.fZeroInitialized) {
- SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, SK_ColorTRANSPARENT, NULL);
- }
-
- while (true) {
- // Every entry takes at least two bytes
- if ((int) totalBytes - currByte < 2) {
- SkCodecPrintf("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))) {
- SkCodecPrintf("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) {
- SkCodecPrintf("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) {
- SkCodecPrintf("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)) {
- SkCodecPrintf("Warning: invalid RLE input.\n");
- return kIncompleteInput;
- }
- // Set numPixels number of pixels
- while (numPixels > 0) {
- switch(fBitsPerPixel) {
- case 4: {
- SkASSERT(currByte < totalBytes);
- uint8_t val = buffer.get()[currByte++];
- setRLEPixel(dst, dstRowBytes, dstInfo, x++,
- y, val >> 4);
- numPixels--;
- if (numPixels != 0) {
- setRLEPixel(dst, dstRowBytes, dstInfo,
- x++, y, val & 0xF);
- numPixels--;
- }
- break;
- }
- case 8:
- SkASSERT(currByte < totalBytes);
- setRLEPixel(dst, dstRowBytes, dstInfo, 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++];
- setRLE24Pixel(dst, dstRowBytes, dstInfo,
- x++, y, red, green, blue);
- 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) {
- SkCodecPrintf("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++];
- while (x < endX) {
- setRLE24Pixel(dst, dstRowBytes, dstInfo, x++, y, red,
- green, blue);
- }
- } 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(dst, dstRowBytes, dstInfo, 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,
- const Options& opts) {
- // Set constant values
- const int width = dstInfo.width();
- const int height = dstInfo.height();
- const size_t rowBytes = SkAlign4(compute_row_bytes(width, fBitsPerPixel));
-
- // Get swizzler configuration and choose the fill value for failures. We will use
- // zero as the default palette index, black for opaque images, and transparent for
- // non-opaque images.
- SkSwizzler::SrcConfig config;
- uint32_t fillColorOrIndex;
- bool zeroFill = true;
- switch (fBitsPerPixel) {
- case 1:
- config = SkSwizzler::kIndex1;
- fillColorOrIndex = 0;
- break;
- case 2:
- config = SkSwizzler::kIndex2;
- fillColorOrIndex = 0;
- break;
- case 4:
- config = SkSwizzler::kIndex4;
- fillColorOrIndex = 0;
- break;
- case 8:
- config = SkSwizzler::kIndex;
- fillColorOrIndex = 0;
- break;
- case 24:
- config = SkSwizzler::kBGR;
- fillColorOrIndex = SK_ColorBLACK;
- zeroFill = false;
- break;
- case 32:
- if (kOpaque_SkAlphaType == dstInfo.alphaType()) {
- config = SkSwizzler::kBGRX;
- fillColorOrIndex = SK_ColorBLACK;
- zeroFill = false;
- } else {
- config = SkSwizzler::kBGRA;
- fillColorOrIndex = SK_ColorTRANSPARENT;
- }
- break;
- default:
- SkASSERT(false);
- return kInvalidInput;
- }
-
- // Get a pointer to the color table if it exists
- const SkPMColor* colorPtr = NULL != fColorTable.get() ? fColorTable->readColors() : NULL;
-
- // Create swizzler
- SkAutoTDelete<SkSwizzler> swizzler(SkSwizzler::CreateSwizzler(config,
- colorPtr, dstInfo, kNo_ZeroInitialized));
-
- // 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) {
- SkCodecPrintf("Warning: incomplete input stream.\n");
- // Fill the destination image on failure
- if (kNo_ZeroInitialized == opts.fZeroInitialized || !zeroFill) {
- void* dstStart = get_dst_start_row(dst, dstRowBytes, y, fRowOrder);
- SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height() - y,
- fillColorOrIndex, colorPtr);
- }
- return kIncompleteInput;
- }
-
- // Decode the row in destination format
- uint32_t row;
- if (kTopDown_RowOrder == fRowOrder) {
- row = y;
- } else {
- row = height - 1 - y;
- }
-
- void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * row);
- swizzler->swizzle(dstRow, srcBuffer.get());
- // FIXME: SkSwizzler::ResultAlpha r =
- // swizzler->swizzle(dstRow, srcBuffer.get());
- // 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
- // in the kN32 case.
- /*
- 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);
- }
- }
- }
- */
-
- // Finally, apply the AND mask for bmp-in-ico images
- if (fIsIco) {
- // The AND mask is always 1 bit per pixel
- const size_t rowBytes = SkAlign4(compute_row_bytes(width, 1));
-
- SkPMColor* dstPtr = (SkPMColor*) dst;
- for (int y = 0; y < height; y++) {
- // The srcBuffer will at least be large enough
- if (stream()->read(srcBuffer.get(), rowBytes) != rowBytes) {
- SkCodecPrintf("Warning: incomplete AND mask for bmp-in-ico.\n");
- return kIncompleteInput;
- }
-
- int row;
- if (kBottomUp_RowOrder == fRowOrder) {
- row = height - y - 1;
- } else {
- row = y;
- }
-
- SkPMColor* dstRow =
- SkTAddOffset<SkPMColor>(dstPtr, row * dstRowBytes);
-
- for (int x = 0; x < width; x++) {
- int quotient;
- int modulus;
- SkTDivMod(x, 8, "ient, &modulus);
- uint32_t shift = 7 - modulus;
- uint32_t alphaBit =
- (srcBuffer.get()[quotient] >> shift) & 0x1;
- dstRow[x] &= alphaBit - 1;
- }
- }
- }
-
- // Finished decoding the entire image
- return kSuccess;
+ return numColors;
}
diff --git a/src/codec/SkBmpCodec.h b/src/codec/SkBmpCodec.h
index 9909945..51d0300 100644
--- a/src/codec/SkBmpCodec.h
+++ b/src/codec/SkBmpCodec.h
@@ -4,6 +4,8 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#ifndef SkBmpCodec_DEFINED
+#define SkBmpCodec_DEFINED
#include "SkCodec.h"
#include "SkColorTable.h"
@@ -13,19 +15,15 @@
#include "SkSwizzler.h"
#include "SkTypes.h"
-// TODO: rename SkCodec_libbmp files to SkBmpCodec
/*
- *
- * This class implements the decoding for bmp images
- *
+ * This class enables code sharing between its bmp codec subclasses. The
+ * subclasses actually do the work.
*/
class SkBmpCodec : public SkCodec {
public:
/*
- *
* Describes if rows of the input start at the top or bottom of the image
- *
*/
enum RowOrder {
kTopDown_RowOrder,
@@ -33,161 +31,75 @@
};
/*
- *
* Checks the start of the stream to see if the image is a bmp
- *
*/
static bool IsBmp(SkStream*);
/*
- *
* Assumes IsBmp was called and returned true
* Creates a bmp decoder
* Reads enough of the stream to determine the image format
- *
*/
static SkCodec* NewFromStream(SkStream*);
/*
- *
* Creates a bmp decoder for a bmp embedded in ico
* Reads enough of the stream to determine the image format
- *
*/
static SkCodec* NewFromIco(SkStream*);
protected:
- /*
- *
- * Initiates the bmp decode
- *
- */
- Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
- size_t dstRowBytes, const Options&, SkPMColor*,
- int*) override;
+ SkBmpCodec(const SkImageInfo& info, SkStream* stream, uint16_t bitsPerPixel,
+ RowOrder rowOrder);
SkEncodedFormat onGetEncodedFormat() const override { return kBMP_SkEncodedFormat; }
-private:
-
/*
- *
- * Used to define the input format of the bmp
- *
- */
- enum BitmapInputFormat {
- kStandard_BitmapInputFormat,
- kRLE_BitmapInputFormat,
- kBitMask_BitmapInputFormat,
- kUnknown_BitmapInputFormat
- };
-
- /*
- *
- * Creates the color table
- * Sets colorCount to the new color count if it is non-NULL
- */
- bool createColorTable(SkAlphaType alphaType, int* colorCount);
-
- /*
- *
- * Creates a bmp decoder
- * Reads enough of the stream to determine the image format
- *
- */
- static SkCodec* NewFromStream(SkStream*, bool isIco);
-
- /*
- *
* Read enough of the stream to initialize the SkBmpCodec. Returns a bool
* representing success or failure. If it returned true, and codecOut was
* not NULL, it will be set to a new SkBmpCodec.
* Does *not* take ownership of the passed in SkStream.
- *
*/
- static bool ReadHeader(SkStream*, bool isIco, SkCodec** codecOut);
+ static bool ReadHeader(SkStream*, bool inIco, SkCodec** codecOut);
/*
- *
- * Performs the bitmap decoding for bit masks input format
- *
+ * Rewinds the image stream if necessary
*/
- Result decodeMask(const SkImageInfo& dstInfo, void* dst,
- size_t dstRowBytes, const Options& opts);
+ bool handleRewind(bool inIco);
/*
- *
- * Set an RLE pixel using the color table
- *
+ * Get the destination row to start filling from
+ * Used to fill the remainder of the image on incomplete input for bmps
+ * This is tricky since bmps may be kTopDown or kBottomUp. For kTopDown,
+ * we start filling from where we left off, but for kBottomUp we start
+ * filling at the top of the image.
*/
- void setRLEPixel(void* dst, size_t dstRowBytes,
- const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
- uint8_t index);
- /*
- *
- * Set an RLE24 pixel from R, G, B values
- *
- */
- void setRLE24Pixel(void* dst, size_t dstRowBytes,
- const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
- uint8_t red, uint8_t green, uint8_t blue);
+ void* getDstStartRow(void* dst, size_t dstRowBytes, int32_t y) const;
/*
- *
- * Performs the bitmap decoding for RLE input format
- *
+ * Compute the number of colors in the color table
*/
- Result decodeRLE(const SkImageInfo& dstInfo, void* dst,
- size_t dstRowBytes, const Options& opts);
+ uint32_t computeNumColors(uint32_t numColors);
/*
- *
- * Performs the bitmap decoding for standard input format
- *
+ * Accessors used by subclasses
*/
- Result decode(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts);
+ uint16_t bitsPerPixel() const { return fBitsPerPixel; }
+ RowOrder rowOrder() const { return fRowOrder; }
+
+private:
/*
- *
- * 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 numColors the number of colors in the color table
- * @param bytesPerColor the number of bytes in the stream used to represent
- each color in the color table
- * @param offset the offset of the image pixel data from the end of the
- * headers
- * @param rowOrder indicates whether rows are ordered top-down or bottom-up
- * @param RLEBytes 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
- *
+ * Creates a bmp decoder
+ * Reads enough of the stream to determine the image format
*/
- SkBmpCodec(const SkImageInfo& srcInfo, SkStream* stream,
- uint16_t bitsPerPixel, BitmapInputFormat format,
- SkMasks* masks, uint32_t numColors, uint32_t bytesPerColor,
- uint32_t offset, RowOrder rowOrder, size_t RLEBytes,
- bool isIco);
+ static SkCodec* NewFromStream(SkStream*, bool inIco);
- // Fields
- const uint16_t fBitsPerPixel;
- const BitmapInputFormat fInputFormat;
- SkAutoTDelete<SkMasks> fMasks; // owned
- SkAutoTUnref<SkColorTable> fColorTable; // owned
- uint32_t fNumColors;
- const uint32_t fBytesPerColor;
- const uint32_t fOffset;
- const RowOrder fRowOrder;
- const size_t fRLEBytes;
- const bool fIsIco;
+ const uint16_t fBitsPerPixel;
+ const RowOrder fRowOrder;
typedef SkCodec INHERITED;
};
+
+#endif
diff --git a/src/codec/SkBmpMaskCodec.cpp b/src/codec/SkBmpMaskCodec.cpp
new file mode 100644
index 0000000..f30266b
--- /dev/null
+++ b/src/codec/SkBmpMaskCodec.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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 "SkBmpMaskCodec.h"
+#include "SkCodecPriv.h"
+#include "SkColorPriv.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) {
+ // Ensure that the profile type is unchanged
+ if (dst.profileType() != src.profileType()) {
+ return false;
+ }
+
+ // Ensure the alpha type is valid
+ if (!valid_alpha(dst.alphaType(), src.alphaType())) {
+ return false;
+ }
+
+ // Check for supported color types
+ switch (dst.colorType()) {
+ // Allow output to kN32
+ case kN32_SkColorType:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+/*
+ * Creates an instance of the decoder
+ */
+SkBmpMaskCodec::SkBmpMaskCodec(const SkImageInfo& info, SkStream* stream,
+ uint16_t bitsPerPixel, SkMasks* masks,
+ SkBmpCodec::RowOrder rowOrder)
+ : INHERITED(info, stream, bitsPerPixel, rowOrder)
+ , fMasks(masks)
+ , fMaskSwizzler(NULL)
+ , fSrcBuffer(NULL)
+{}
+
+/*
+ * Initiates the bitmap decode
+ */
+SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo,
+ void* dst, size_t dstRowBytes,
+ const Options& opts,
+ SkPMColor* inputColorPtr,
+ int* inputColorCount) {
+ if (!this->handleRewind(false)) {
+ return kCouldNotRewind;
+ }
+ if (opts.fSubset) {
+ // Subsets are not supported.
+ return kUnimplemented;
+ }
+ if (dstInfo.dimensions() != this->getInfo().dimensions()) {
+ SkCodecPrintf("Error: scaling not supported.\n");
+ return kInvalidScale;
+ }
+
+ if (!conversion_possible(dstInfo, this->getInfo())) {
+ SkCodecPrintf("Error: cannot convert input type to output type.\n");
+ return kInvalidConversion;
+ }
+
+ // Initialize a the mask swizzler
+ if (!this->initializeSwizzler(dstInfo)) {
+ SkCodecPrintf("Error: cannot initialize swizzler.\n");
+ return kInvalidConversion;
+ }
+
+ return this->decode(dstInfo, dst, dstRowBytes, opts);
+}
+
+bool SkBmpMaskCodec::initializeSwizzler(const SkImageInfo& dstInfo) {
+ // Allocate space for a row buffer
+ const size_t rowBytes = SkAlign4(compute_row_bytes(dstInfo.width(), this->bitsPerPixel()));
+ fSrcBuffer.reset(SkNEW_ARRAY(uint8_t, rowBytes));
+
+ // Create the swizzler
+ fMaskSwizzler.reset(SkMaskSwizzler::CreateMaskSwizzler(
+ dstInfo, fMasks, this->bitsPerPixel()));
+
+ if (NULL == fMaskSwizzler.get()) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Performs the decoding
+ */
+SkCodec::Result SkBmpMaskCodec::decode(const SkImageInfo& dstInfo,
+ void* dst, size_t dstRowBytes,
+ const Options& opts) {
+ // Set constant values
+ const int width = dstInfo.width();
+ const int height = dstInfo.height();
+ const size_t rowBytes = SkAlign4(compute_row_bytes(width, this->bitsPerPixel()));
+
+ // Iterate over rows of the image
+ uint8_t* srcRow = fSrcBuffer.get();
+ for (int y = 0; y < height; y++) {
+ // Read a row of the input
+ if (this->stream()->read(srcRow, rowBytes) != rowBytes) {
+ SkCodecPrintf("Warning: incomplete input stream.\n");
+ // Fill the destination image on failure
+ SkPMColor fillColor = dstInfo.alphaType() == kOpaque_SkAlphaType ?
+ SK_ColorBLACK : SK_ColorTRANSPARENT;
+ if (kNo_ZeroInitialized == opts.fZeroInitialized || 0 != fillColor) {
+ void* dstStart = this->getDstStartRow(dst, dstRowBytes, y);
+ SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height() - y, fillColor,
+ NULL);
+ }
+ return kIncompleteInput;
+ }
+
+ // Decode the row in destination format
+ int row = SkBmpCodec::kBottomUp_RowOrder == this->rowOrder() ? height - 1 - y : y;
+ void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes);
+ fMaskSwizzler->swizzle(dstRow, srcRow);
+ }
+
+ // Finished decoding the entire image
+ return kSuccess;
+}
diff --git a/src/codec/SkBmpMaskCodec.h b/src/codec/SkBmpMaskCodec.h
new file mode 100644
index 0000000..6f43bb3
--- /dev/null
+++ b/src/codec/SkBmpMaskCodec.h
@@ -0,0 +1,52 @@
+/*
+ * 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 "SkBmpCodec.h"
+#include "SkImageInfo.h"
+#include "SkMaskSwizzler.h"
+#include "SkTypes.h"
+
+/*
+ * This class implements the decoding for bmp images using bit masks
+ */
+class SkBmpMaskCodec : public SkBmpCodec {
+public:
+
+ /*
+ * Creates an instance of the decoder
+ *
+ * Called only by SkBmpCodec::NewFromStream
+ * There should be no other callers despite this being public
+ *
+ * @param srcInfo contains the source width and height
+ * @param stream the stream of encoded image data
+ * @param bitsPerPixel the number of bits used to store each pixel
+ * @param masks color masks for certain bmp formats
+ * @param rowOrder indicates whether rows are ordered top-down or bottom-up
+ */
+ SkBmpMaskCodec(const SkImageInfo& srcInfo, SkStream* stream,
+ uint16_t bitsPerPixel, SkMasks* masks, RowOrder rowOrder);
+
+protected:
+
+ Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
+ size_t dstRowBytes, const Options&, SkPMColor*,
+ int*) override;
+
+private:
+
+ bool initializeSwizzler(const SkImageInfo& dstInfo);
+
+ Result decode(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
+ const Options& opts);
+
+ SkAutoTDelete<SkMasks> fMasks; // owned
+ SkAutoTDelete<SkMaskSwizzler> fMaskSwizzler;
+ SkAutoTDeleteArray<uint8_t> fSrcBuffer;
+
+ typedef SkBmpCodec INHERITED;
+};
diff --git a/src/codec/SkBmpRLECodec.cpp b/src/codec/SkBmpRLECodec.cpp
new file mode 100644
index 0000000..828871c
--- /dev/null
+++ b/src/codec/SkBmpRLECodec.cpp
@@ -0,0 +1,430 @@
+/*
+ * 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 "SkBmpRLECodec.h"
+#include "SkCodecPriv.h"
+#include "SkColorPriv.h"
+#include "SkScanlineDecoder.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) {
+ // Ensure that the profile type is unchanged
+ if (dst.profileType() != src.profileType()) {
+ return false;
+ }
+
+ // Ensure the alpha type is valid
+ if (!valid_alpha(dst.alphaType(), src.alphaType())) {
+ return false;
+ }
+
+ // Check for supported color types
+ switch (dst.colorType()) {
+ // Allow output to kN32 from any type of input
+ case kN32_SkColorType:
+ return true;
+ // Allow output to kIndex_8 from compatible inputs
+ case kIndex_8_SkColorType:
+ return kIndex_8_SkColorType == src.colorType();
+ default:
+ return false;
+ }
+}
+
+/*
+ * Creates an instance of the decoder
+ * Called only by NewFromStream
+ */
+SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream,
+ uint16_t bitsPerPixel, uint32_t numColors,
+ uint32_t bytesPerColor, uint32_t offset,
+ SkBmpCodec::RowOrder rowOrder, size_t RLEBytes)
+ : INHERITED(info, stream, bitsPerPixel, rowOrder)
+ , fColorTable(NULL)
+ , fNumColors(this->computeNumColors(numColors))
+ , fBytesPerColor(bytesPerColor)
+ , fOffset(offset)
+ , fStreamBuffer(SkNEW_ARRAY(uint8_t, RLEBytes))
+ , fRLEBytes(RLEBytes)
+ , fCurrRLEByte(0)
+{}
+
+/*
+ * Initiates the bitmap decode
+ */
+SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo,
+ void* dst, size_t dstRowBytes,
+ const Options& opts,
+ SkPMColor* inputColorPtr,
+ int* inputColorCount) {
+ if (!this->handleRewind(false)) {
+ return kCouldNotRewind;
+ }
+ if (opts.fSubset) {
+ // Subsets are not supported.
+ return kUnimplemented;
+ }
+ if (dstInfo.dimensions() != this->getInfo().dimensions()) {
+ SkCodecPrintf("Error: scaling not supported.\n");
+ return kInvalidScale;
+ }
+ if (!conversion_possible(dstInfo, this->getInfo())) {
+ SkCodecPrintf("Error: cannot convert input type to output type.\n");
+ return kInvalidConversion;
+ }
+
+ // Create the color table if necessary and prepare the stream for decode
+ // Note that if it is non-NULL, inputColorCount will be modified
+ if (!this->createColorTable(inputColorCount)) {
+ SkCodecPrintf("Error: could not create color table.\n");
+ return kInvalidInput;
+ }
+
+ // Copy the color table to the client if necessary
+ copy_color_table(dstInfo, fColorTable, inputColorPtr, inputColorCount);
+
+ // Initialize a swizzler if necessary
+ if (!this->initializeStreamBuffer()) {
+ SkCodecPrintf("Error: cannot initialize swizzler.\n");
+ return kInvalidConversion;
+ }
+
+ // Perform the decode
+ return decode(dstInfo, dst, dstRowBytes, opts);
+}
+
+/*
+ * Process the color table for the bmp input
+ */
+ bool SkBmpRLECodec::createColorTable(int* numColors) {
+ // Allocate memory for color table
+ uint32_t colorBytes = 0;
+ SkPMColor colorTable[256];
+ if (this->bitsPerPixel() <= 8) {
+ // Inform the caller of the number of colors
+ uint32_t maxColors = 1 << this->bitsPerPixel();
+ if (NULL != numColors) {
+ // We set the number of colors to maxColors in order to ensure
+ // safe memory accesses. Otherwise, an invalid pixel could
+ // access memory outside of our color table array.
+ *numColors = maxColors;
+ }
+
+ // Read the color table from the stream
+ colorBytes = fNumColors * fBytesPerColor;
+ SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes));
+ if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
+ SkCodecPrintf("Error: unable to read color table.\n");
+ return false;
+ }
+
+ // Fill in the color table
+ uint32_t i = 0;
+ for (; i < fNumColors; i++) {
+ uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
+ uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
+ uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
+ colorTable[i] = SkPackARGB32NoCheck(0xFF, 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);
+ }
+
+ // Set the color table
+ fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorTable, maxColors)));
+ }
+
+ // Check that we have not read past the pixel array offset
+ if(fOffset < colorBytes) {
+ // 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.
+ SkCodecPrintf("Error: pixel data offset less than color table size.\n");
+ return false;
+ }
+
+ // After reading the color table, skip to the start of the pixel array
+ if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
+ SkCodecPrintf("Error: unable to skip to image data.\n");
+ return false;
+ }
+
+ // Return true on success
+ return true;
+}
+
+bool SkBmpRLECodec::initializeStreamBuffer() {
+ // Setup a buffer to contain the full input stream
+ size_t totalBytes = this->stream()->read(fStreamBuffer.get(), fRLEBytes);
+ if (totalBytes < fRLEBytes) {
+ fRLEBytes = totalBytes;
+ SkCodecPrintf("Warning: incomplete RLE file.\n");
+ }
+ if (fRLEBytes == 0) {
+ SkCodecPrintf("Error: could not read RLE image data.\n");
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Set an RLE pixel using the color table
+ */
+void SkBmpRLECodec::setPixel(void* dst, size_t dstRowBytes,
+ const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
+ uint8_t index) {
+ // Set the row
+ int height = dstInfo.height();
+ int row;
+ if (SkBmpCodec::kBottomUp_RowOrder == this->rowOrder()) {
+ row = height - y - 1;
+ } else {
+ row = y;
+ }
+
+ // Set the pixel based on destination color type
+ switch (dstInfo.colorType()) {
+ case kN32_SkColorType: {
+ SkPMColor* dstRow = SkTAddOffset<SkPMColor>((SkPMColor*) dst,
+ row * (int) dstRowBytes);
+ dstRow[x] = fColorTable->operator[](index);
+ break;
+ }
+ default:
+ // This case should not be reached. We should catch an invalid
+ // color type when we check that the conversion is possible.
+ SkASSERT(false);
+ break;
+ }
+}
+
+/*
+ * Set an RLE pixel from R, G, B values
+ */
+void SkBmpRLECodec::setRGBPixel(void* dst, size_t dstRowBytes,
+ const SkImageInfo& dstInfo, uint32_t x,
+ uint32_t y, uint8_t red, uint8_t green,
+ uint8_t blue) {
+ // Set the row
+ int height = dstInfo.height();
+ int row;
+ if (SkBmpCodec::kBottomUp_RowOrder == this->rowOrder()) {
+ row = height - y - 1;
+ } else {
+ row = y;
+ }
+
+ // Set the pixel based on destination color type
+ switch (dstInfo.colorType()) {
+ case kN32_SkColorType: {
+ SkPMColor* dstRow = SkTAddOffset<SkPMColor>((SkPMColor*) dst,
+ row * (int) dstRowBytes);
+ dstRow[x] = SkPackARGB32NoCheck(0xFF, red, green, blue);
+ break;
+ }
+ default:
+ // This case should not be reached. We should catch an invalid
+ // color type when we check that the conversion is possible.
+ SkASSERT(false);
+ break;
+ }
+}
+
+/*
+ * 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 SkBmpRLECodec::decode(const SkImageInfo& dstInfo,
+ void* dst, size_t dstRowBytes,
+ const Options& opts) {
+ // 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();
+
+ // Destination parameters
+ int x = 0;
+ int y = 0;
+
+ // Set the background as transparent. Then, if the RLE code skips pixels,
+ // the skipped pixels will be transparent.
+ // Because of the need for transparent pixels, kN32 is the only color
+ // type that makes sense for the destination format.
+ SkASSERT(kN32_SkColorType == dstInfo.colorType());
+ if (kNo_ZeroInitialized == opts.fZeroInitialized) {
+ SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, SK_ColorTRANSPARENT, NULL);
+ }
+
+ while (true) {
+ // If we have reached a row that is beyond the requested height, we have
+ // succeeded.
+ if (y >= height) {
+ // It would be better to check for the EOF marker before returning
+ // success, but we may be performing a scanline decode, which
+ // may require us to stop before decoding the full height.
+ return kSuccess;
+ }
+
+ // Every entry takes at least two bytes
+ if ((int) fRLEBytes - fCurrRLEByte < 2) {
+ SkCodecPrintf("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 = fStreamBuffer.get()[fCurrRLEByte++];
+ const uint8_t task = fStreamBuffer.get()[fCurrRLEByte++];
+
+ // 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) fRLEBytes - fCurrRLEByte < 2) {
+ SkCodecPrintf("Warning: incomplete RLE input\n");
+ return kIncompleteInput;
+ }
+ // Modify x and y
+ const uint8_t dx = fStreamBuffer.get()[fCurrRLEByte++];
+ const uint8_t dy = fStreamBuffer.get()[fCurrRLEByte++];
+ x += dx;
+ y += dy;
+ if (x > width || y > height) {
+ SkCodecPrintf("Warning: invalid RLE input 1.\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,
+ this->bitsPerPixel());
+ // 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) fRLEBytes - fCurrRLEByte < SkAlign2(rowBytes)) {
+ SkCodecPrintf("Warning: invalid RLE input 2.\n");
+ return kIncompleteInput;
+ }
+ // Set numPixels number of pixels
+ while (numPixels > 0) {
+ switch(this->bitsPerPixel()) {
+ case 4: {
+ SkASSERT(fCurrRLEByte < fRLEBytes);
+ uint8_t val = fStreamBuffer.get()[fCurrRLEByte++];
+ setPixel(dst, dstRowBytes, dstInfo, x++,
+ y, val >> 4);
+ numPixels--;
+ if (numPixels != 0) {
+ setPixel(dst, dstRowBytes, dstInfo,
+ x++, y, val & 0xF);
+ numPixels--;
+ }
+ break;
+ }
+ case 8:
+ SkASSERT(fCurrRLEByte < fRLEBytes);
+ setPixel(dst, dstRowBytes, dstInfo, x++,
+ y, fStreamBuffer.get()[fCurrRLEByte++]);
+ numPixels--;
+ break;
+ case 24: {
+ SkASSERT(fCurrRLEByte + 2 < fRLEBytes);
+ uint8_t blue = fStreamBuffer.get()[fCurrRLEByte++];
+ uint8_t green = fStreamBuffer.get()[fCurrRLEByte++];
+ uint8_t red = fStreamBuffer.get()[fCurrRLEByte++];
+ setRGBPixel(dst, dstRowBytes, dstInfo,
+ x++, y, red, green, blue);
+ numPixels--;
+ }
+ default:
+ SkASSERT(false);
+ return kInvalidInput;
+ }
+ }
+ // Skip a byte if necessary to maintain alignment
+ if (!SkIsAlign2(rowBytes)) {
+ fCurrRLEByte++;
+ }
+ 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 == this->bitsPerPixel()) {
+ // 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) fRLEBytes - fCurrRLEByte < 2) {
+ SkCodecPrintf("Warning: incomplete RLE input\n");
+ return kIncompleteInput;
+ }
+
+ // Fill the pixels up to endX with the specified color
+ uint8_t blue = task;
+ uint8_t green = fStreamBuffer.get()[fCurrRLEByte++];
+ uint8_t red = fStreamBuffer.get()[fCurrRLEByte++];
+ while (x < endX) {
+ setRGBPixel(dst, dstRowBytes, dstInfo, x++, y, red,
+ green, blue);
+ }
+ } 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 == this->bitsPerPixel()) {
+ indices[0] >>= 4;
+ indices[1] &= 0xf;
+ }
+
+ // Set the indicated number of pixels
+ for (int which = 0; x < endX; x++) {
+ setPixel(dst, dstRowBytes, dstInfo, x, y,
+ indices[which]);
+ which = !which;
+ }
+ }
+ }
+ }
+}
diff --git a/src/codec/SkBmpRLECodec.h b/src/codec/SkBmpRLECodec.h
new file mode 100644
index 0000000..f1f1ae9
--- /dev/null
+++ b/src/codec/SkBmpRLECodec.h
@@ -0,0 +1,86 @@
+/*
+ * 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 "SkBmpCodec.h"
+#include "SkColorTable.h"
+#include "SkImageInfo.h"
+#include "SkTypes.h"
+
+/*
+ * This class implements the decoding for bmp images that use an RLE encoding
+ */
+class SkBmpRLECodec : public SkBmpCodec {
+public:
+
+ /*
+ * Creates an instance of the decoder
+ *
+ * Called only by SkBmpCodec::NewFromStream
+ * There should be no other callers despite this being public
+ *
+ * @param srcInfo contains the source width and height
+ * @param stream the stream of encoded image data
+ * @param bitsPerPixel the number of bits used to store each pixel
+ * @param numColors the number of colors in the color table
+ * @param bytesPerColor the number of bytes in the stream used to represent
+ each color in the color table
+ * @param offset the offset of the image pixel data from the end of the
+ * headers
+ * @param rowOrder indicates whether rows are ordered top-down or bottom-up
+ * @param RLEBytes indicates the amount of data left in the stream
+ * after decoding the headers
+ */
+ SkBmpRLECodec(const SkImageInfo& srcInfo, SkStream* stream,
+ uint16_t bitsPerPixel, uint32_t numColors,
+ uint32_t bytesPerColor, uint32_t offset,
+ SkBmpCodec::RowOrder rowOrder, size_t RLEBytes);
+
+protected:
+
+ Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
+ size_t dstRowBytes, const Options&, SkPMColor*,
+ int*) override;
+
+private:
+
+ /*
+ * Creates the color table
+ * Sets colorCount to the new color count if it is non-NULL
+ */
+ bool createColorTable(int* colorCount);
+
+ bool initializeStreamBuffer();
+
+ /*
+ * Set an RLE pixel using the color table
+ */
+ void setPixel(void* dst, size_t dstRowBytes,
+ const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
+ uint8_t index);
+ /*
+ * Set an RLE24 pixel from R, G, B values
+ */
+ void setRGBPixel(void* dst, size_t dstRowBytes,
+ const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
+ uint8_t red, uint8_t green, uint8_t blue);
+
+ /*
+ * Performs the bitmap decoding for RLE input format
+ */
+ Result decode(const SkImageInfo& dstInfo, void* dst,
+ size_t dstRowBytes, const Options& opts);
+
+ SkAutoTUnref<SkColorTable> fColorTable; // owned
+ const uint32_t fNumColors;
+ const uint32_t fBytesPerColor;
+ const uint32_t fOffset;
+ SkAutoTDeleteArray<uint8_t> fStreamBuffer;
+ size_t fRLEBytes;
+ uint32_t fCurrRLEByte;
+
+ typedef SkBmpCodec INHERITED;
+};
diff --git a/src/codec/SkBmpStandardCodec.cpp b/src/codec/SkBmpStandardCodec.cpp
new file mode 100644
index 0000000..70535a7
--- /dev/null
+++ b/src/codec/SkBmpStandardCodec.cpp
@@ -0,0 +1,361 @@
+/*
+ * 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 "SkBmpStandardCodec.h"
+#include "SkCodecPriv.h"
+#include "SkColorPriv.h"
+#include "SkScanlineDecoder.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) {
+ // Ensure that the profile type is unchanged
+ if (dst.profileType() != src.profileType()) {
+ return false;
+ }
+
+ // Ensure the alpha type is valid
+ if (!valid_alpha(dst.alphaType(), src.alphaType())) {
+ return false;
+ }
+
+ // Check for supported color types
+ switch (dst.colorType()) {
+ // Allow output to kN32 from any type of input
+ case kN32_SkColorType:
+ return true;
+ // Allow output to kIndex_8 from compatible inputs
+ case kIndex_8_SkColorType:
+ return kIndex_8_SkColorType == src.colorType();
+ default:
+ return false;
+ }
+}
+
+/*
+ * Creates an instance of the decoder
+ * Called only by NewFromStream
+ */
+SkBmpStandardCodec::SkBmpStandardCodec(const SkImageInfo& info, SkStream* stream,
+ uint16_t bitsPerPixel, uint32_t numColors,
+ uint32_t bytesPerColor, uint32_t offset,
+ SkBmpCodec::RowOrder rowOrder, bool inIco)
+ : INHERITED(info, stream, bitsPerPixel, rowOrder)
+ , fColorTable(NULL)
+ , fNumColors(this->computeNumColors(numColors))
+ , fBytesPerColor(bytesPerColor)
+ , fOffset(offset)
+ , fSwizzler(NULL)
+ , fSrcBuffer(NULL)
+ , fInIco(inIco)
+{}
+
+/*
+ * Initiates the bitmap decode
+ */
+SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo,
+ void* dst, size_t dstRowBytes,
+ const Options& opts,
+ SkPMColor* inputColorPtr,
+ int* inputColorCount) {
+ if (!this->handleRewind(fInIco)) {
+ return kCouldNotRewind;
+ }
+ if (opts.fSubset) {
+ // Subsets are not supported.
+ return kUnimplemented;
+ }
+ if (dstInfo.dimensions() != this->getInfo().dimensions()) {
+ SkCodecPrintf("Error: scaling not supported.\n");
+ return kInvalidScale;
+ }
+ if (!conversion_possible(dstInfo, this->getInfo())) {
+ SkCodecPrintf("Error: cannot convert input type to output type.\n");
+ return kInvalidConversion;
+ }
+
+ // Create the color table if necessary and prepare the stream for decode
+ // Note that if it is non-NULL, inputColorCount will be modified
+ if (!this->createColorTable(dstInfo.alphaType(), inputColorCount)) {
+ SkCodecPrintf("Error: could not create color table.\n");
+ return kInvalidInput;
+ }
+
+ // Copy the color table to the client if necessary
+ copy_color_table(dstInfo, fColorTable, inputColorPtr, inputColorCount);
+
+ // Initialize a swizzler if necessary
+ if (!this->initializeSwizzler(dstInfo, opts)) {
+ SkCodecPrintf("Error: cannot initialize swizzler.\n");
+ return kInvalidConversion;
+ }
+
+ return this->decode(dstInfo, dst, dstRowBytes, opts);
+}
+
+/*
+ * Process the color table for the bmp input
+ */
+ bool SkBmpStandardCodec::createColorTable(SkAlphaType alphaType, int* numColors) {
+ // Allocate memory for color table
+ uint32_t colorBytes = 0;
+ SkPMColor colorTable[256];
+ if (this->bitsPerPixel() <= 8) {
+ // Inform the caller of the number of colors
+ uint32_t maxColors = 1 << this->bitsPerPixel();
+ if (NULL != numColors) {
+ // We set the number of colors to maxColors in order to ensure
+ // safe memory accesses. Otherwise, an invalid pixel could
+ // access memory outside of our color table array.
+ *numColors = maxColors;
+ }
+
+ // Read the color table from the stream
+ colorBytes = fNumColors * fBytesPerColor;
+ SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes));
+ if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
+ SkCodecPrintf("Error: unable to read color table.\n");
+ return false;
+ }
+
+ // Choose the proper packing function
+ SkPMColor (*packARGB) (uint32_t, uint32_t, uint32_t, uint32_t);
+ switch (alphaType) {
+ case kOpaque_SkAlphaType:
+ case kUnpremul_SkAlphaType:
+ packARGB = &SkPackARGB32NoCheck;
+ break;
+ case kPremul_SkAlphaType:
+ packARGB = &SkPreMultiplyARGB;
+ break;
+ default:
+ // This should not be reached because conversion possible
+ // should fail if the alpha type is not one of the above
+ // values.
+ SkASSERT(false);
+ packARGB = NULL;
+ break;
+ }
+
+ // Fill in the color table
+ uint32_t i = 0;
+ for (; i < fNumColors; i++) {
+ uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
+ uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
+ uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
+ uint8_t alpha;
+ if (kOpaque_SkAlphaType == alphaType) {
+ alpha = 0xFF;
+ } else {
+ alpha = get_byte(cBuffer.get(), i*fBytesPerColor + 3);
+ }
+ colorTable[i] = packARGB(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);
+ }
+
+ // Set the color table
+ fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorTable, maxColors)));
+ }
+
+ // Bmp-in-Ico files do not use an offset to indicate where the pixel data
+ // begins. Pixel data always begins immediately after the color table.
+ if (!fInIco) {
+ // Check that we have not read past the pixel array offset
+ if(fOffset < colorBytes) {
+ // 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.
+ SkCodecPrintf("Error: pixel data offset less than color table size.\n");
+ return false;
+ }
+
+ // After reading the color table, skip to the start of the pixel array
+ if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
+ SkCodecPrintf("Error: unable to skip to image data.\n");
+ return false;
+ }
+ }
+
+ // Return true on success
+ return true;
+}
+
+bool SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo,
+ const Options& opts) {
+ // Allocate space for a row buffer
+ const size_t rowBytes = SkAlign4(compute_row_bytes(dstInfo.width(), this->bitsPerPixel()));
+ fSrcBuffer.reset(SkNEW_ARRAY(uint8_t, rowBytes));
+
+ // Get swizzler configuration
+ SkSwizzler::SrcConfig config;
+ switch (this->bitsPerPixel()) {
+ 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 (kOpaque_SkAlphaType == dstInfo.alphaType()) {
+ config = SkSwizzler::kBGRX;
+ } else {
+ config = SkSwizzler::kBGRA;
+ }
+ break;
+ default:
+ SkASSERT(false);
+ return false;
+ }
+
+ // Get a pointer to the color table if it exists
+ const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
+
+ // Create swizzler
+ fSwizzler.reset(SkSwizzler::CreateSwizzler(config,
+ colorPtr, dstInfo, opts.fZeroInitialized));
+
+ if (NULL == fSwizzler.get()) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Choose a fill for failures due to an incomplete image. We will use zero as
+ * the default palette index, black for opaque images, and transparent for
+ * non-opaque images.
+ */
+static uint32_t get_fill_color_or_index(uint16_t bitsPerPixels, SkAlphaType alphaType) {
+ uint32_t fillColorOrIndex;
+ switch (bitsPerPixels) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ fillColorOrIndex = 0;
+ break;
+ case 24:
+ fillColorOrIndex = SK_ColorBLACK;
+ break;
+ case 32:
+ if (kOpaque_SkAlphaType == alphaType) {
+ fillColorOrIndex = SK_ColorBLACK;
+ } else {
+ fillColorOrIndex = SK_ColorTRANSPARENT;
+ }
+ break;
+ default:
+ SkASSERT(false);
+ return 0;
+ }
+ return fillColorOrIndex;
+}
+
+/*
+ * Performs the bitmap decoding for standard input format
+ */
+SkCodec::Result SkBmpStandardCodec::decode(const SkImageInfo& dstInfo,
+ void* dst, size_t dstRowBytes,
+ const Options& opts) {
+ // Set constant values
+ const int width = dstInfo.width();
+ const int height = dstInfo.height();
+ const size_t rowBytes = SkAlign4(compute_row_bytes(width, this->bitsPerPixel()));
+
+ // Iterate over rows of the image
+ for (int y = 0; y < height; y++) {
+ // Read a row of the input
+ if (this->stream()->read(fSrcBuffer.get(), rowBytes) != rowBytes) {
+ SkCodecPrintf("Warning: incomplete input stream.\n");
+ // Fill the destination image on failure
+ // Get the fill color/index and check if it is 0
+ uint32_t fillColorOrIndex = get_fill_color_or_index(this->bitsPerPixel(),
+ dstInfo.alphaType());
+ bool zeroFill = (0 == fillColorOrIndex);
+
+ if (kNo_ZeroInitialized == opts.fZeroInitialized || !zeroFill) {
+ // Get a pointer to the color table if it exists
+ const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
+
+ void* dstStart = this->getDstStartRow(dst, dstRowBytes, y);
+ SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height() - y,
+ fillColorOrIndex, colorPtr);
+ }
+ return kIncompleteInput;
+ }
+
+ // Decode the row in destination format
+ uint32_t row;
+ if (SkBmpCodec::kTopDown_RowOrder == this->rowOrder()) {
+ row = y;
+ } else {
+ row = height - 1 - y;
+ }
+
+ void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes);
+ fSwizzler->swizzle(dstRow, fSrcBuffer.get());
+ }
+
+ // Finally, apply the AND mask for bmp-in-ico images
+ if (fInIco) {
+ // The AND mask is always 1 bit per pixel
+ const size_t rowBytes = SkAlign4(compute_row_bytes(width, 1));
+
+ SkPMColor* dstPtr = (SkPMColor*) dst;
+ for (int y = 0; y < height; y++) {
+ // The srcBuffer will at least be large enough
+ if (stream()->read(fSrcBuffer.get(), rowBytes) != rowBytes) {
+ SkCodecPrintf("Warning: incomplete AND mask for bmp-in-ico.\n");
+ return kIncompleteInput;
+ }
+
+ int row;
+ if (SkBmpCodec::kBottomUp_RowOrder == this->rowOrder()) {
+ row = height - y - 1;
+ } else {
+ row = y;
+ }
+
+ SkPMColor* dstRow =
+ SkTAddOffset<SkPMColor>(dstPtr, row * dstRowBytes);
+
+ for (int x = 0; x < width; x++) {
+ int quotient;
+ int modulus;
+ SkTDivMod(x, 8, "ient, &modulus);
+ uint32_t shift = 7 - modulus;
+ uint32_t alphaBit =
+ (fSrcBuffer.get()[quotient] >> shift) & 0x1;
+ dstRow[x] &= alphaBit - 1;
+ }
+ }
+ }
+
+ // Finished decoding the entire image
+ return kSuccess;
+}
diff --git a/src/codec/SkBmpStandardCodec.h b/src/codec/SkBmpStandardCodec.h
new file mode 100644
index 0000000..ff91dcf
--- /dev/null
+++ b/src/codec/SkBmpStandardCodec.h
@@ -0,0 +1,69 @@
+/*
+ * 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 "SkBmpCodec.h"
+#include "SkColorTable.h"
+#include "SkImageInfo.h"
+#include "SkSwizzler.h"
+#include "SkTypes.h"
+
+/*
+ * This class implements the decoding for bmp images that use "standard" modes,
+ * which essentially means they do not contain bit masks or RLE codes.
+ */
+class SkBmpStandardCodec : public SkBmpCodec {
+public:
+
+ /*
+ * Creates an instance of the decoder
+ *
+ * Called only by SkBmpCodec::NewFromStream
+ * There should be no other callers despite this being public
+ *
+ * @param srcInfo contains the source width and height
+ * @param stream the stream of encoded image data
+ * @param bitsPerPixel the number of bits used to store each pixel
+ * @param format the format of the bmp file
+ * @param numColors the number of colors in the color table
+ * @param bytesPerColor the number of bytes in the stream used to represent
+ each color in the color table
+ * @param offset the offset of the image pixel data from the end of the
+ * headers
+ * @param rowOrder indicates whether rows are ordered top-down or bottom-up
+ */
+ SkBmpStandardCodec(const SkImageInfo& srcInfo, SkStream* stream,
+ uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor,
+ uint32_t offset, SkBmpCodec::RowOrder rowOrder, bool isIco);
+
+protected:
+
+ Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
+ size_t dstRowBytes, const Options&, SkPMColor*,
+ int*) override;
+
+private:
+
+ /*
+ * Creates the color table
+ * Sets colorCount to the new color count if it is non-NULL
+ */
+ bool createColorTable(SkAlphaType alphaType, int* colorCount);
+
+ bool initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts);
+
+ Result decode(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts);
+
+ SkAutoTUnref<SkColorTable> fColorTable; // owned
+ const uint32_t fNumColors;
+ const uint32_t fBytesPerColor;
+ const uint32_t fOffset;
+ SkAutoTDelete<SkSwizzler> fSwizzler;
+ SkAutoTDeleteArray<uint8_t> fSrcBuffer;
+ const bool fInIco;
+
+ typedef SkBmpCodec INHERITED;
+};
diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h
index 0c45fdd..2596787 100644
--- a/src/codec/SkCodecPriv.h
+++ b/src/codec/SkCodecPriv.h
@@ -30,6 +30,28 @@
#define COMPUTE_RESULT_ALPHA \
SkSwizzler::GetResult(zeroAlpha, maxAlpha);
+static inline bool valid_alpha(SkAlphaType dstAlpha, SkAlphaType srcAlpha) {
+ // Check for supported alpha types
+ if (srcAlpha != dstAlpha) {
+ if (kOpaque_SkAlphaType == srcAlpha) {
+ // If the source is opaque, we must decode to opaque
+ return false;
+ }
+
+ // The source is not opaque
+ switch (dstAlpha) {
+ case kPremul_SkAlphaType:
+ case kUnpremul_SkAlphaType:
+ // The source is not opaque, so either of these is okay
+ break;
+ default:
+ // We cannot decode a non-opaque image to opaque (or unknown)
+ return false;
+ }
+ }
+ return true;
+}
+
/*
* If there is a color table, get a pointer to the colors, otherwise return NULL
*/
@@ -40,7 +62,6 @@
/*
*
* Copy the codec color table back to the client when kIndex8 color type is requested
- *
*/
static inline void copy_color_table(const SkImageInfo& dstInfo, SkColorTable* colorTable,
SkPMColor* inputColorPtr, int* inputColorCount) {
@@ -53,27 +74,21 @@
}
/*
- *
* 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) {
@@ -88,20 +103,16 @@
}
/*
- *
* 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;
@@ -114,10 +125,8 @@
}
/*
- *
* 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;
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
index 7c6a74c..f239900 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -397,24 +397,11 @@
return false;
}
- // Check for supported alpha types
- if (src.alphaType() != dst.alphaType()) {
- if (kOpaque_SkAlphaType == src.alphaType()) {
- // If the source is opaque, we must decode to opaque
- return false;
- }
-
- // The source is not opaque
- switch (dst.alphaType()) {
- case kPremul_SkAlphaType:
- case kUnpremul_SkAlphaType:
- // The source is not opaque, so either of these is okay
- break;
- default:
- // We cannot decode a non-opaque image to opaque (or unknown)
- return false;
- }
+ // Ensure the alpha type is valid
+ if (!valid_alpha(dst.alphaType(), src.alphaType())) {
+ return false;
}
+
// Check for supported color types
switch (dst.colorType()) {
case kN32_SkColorType:
diff --git a/src/codec/SkMaskSwizzler.h b/src/codec/SkMaskSwizzler.h
index 4ccc3ae..9f4dd44 100644
--- a/src/codec/SkMaskSwizzler.h
+++ b/src/codec/SkMaskSwizzler.h
@@ -4,6 +4,8 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#ifndef SkMaskSwizzler_DEFINED
+#define SkMaskSwizzler_DEFINED
#include "SkMasks.h"
#include "SkSwizzler.h"
@@ -19,37 +21,29 @@
public:
/*
- *
* Create a new swizzler
* @param masks Unowned pointer to helper class
- *
*/
static SkMaskSwizzler* CreateMaskSwizzler(const SkImageInfo& imageInfo,
SkMasks* masks,
uint32_t bitsPerPixel);
/*
- *
* Swizzle a row
- *
*/
SkSwizzler::ResultAlpha swizzle(void* dst, const uint8_t* SK_RESTRICT 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);
@@ -58,3 +52,5 @@
SkMasks* fMasks; // unowned
const RowProc fRowProc;
};
+
+#endif
diff --git a/src/codec/SkMasks.h b/src/codec/SkMasks.h
index 31c6849..0823704 100644
--- a/src/codec/SkMasks.h
+++ b/src/codec/SkMasks.h
@@ -4,6 +4,9 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#ifndef SkMasks_DEFINED
+#define SkMasks_DEFINED
+
#include "SkTypes.h"
/*
@@ -79,3 +82,5 @@
const MaskInfo fBlue;
const MaskInfo fAlpha;
};
+
+#endif