Implementation of image decoding for bmp files, in accordance with the new API.

Currently decodes to opaque and unpremul.

Tested on local test suite.

BUG=skia:3257

Review URL: https://codereview.chromium.org/947283002
This commit is contained in:
msarett 2015-03-16 08:27:53 -07:00 committed by Commit bot
parent dedc2d8e8d
commit 3675874468
14 changed files with 2042 additions and 117 deletions

View 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 {

View 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': {

View 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;

View 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;
for (DecoderProc proc : gDecoderProcs) {
const bool correctFormat = proc.IsFormat(stream);
if (!stream->rewind()) {
return NULL;
}
if (correctFormat) {
return proc.NewFromStream(stream);
}
}
if (isPng) {
streamDeleter.detach();
return SkPngCodec::NewFromStream(stream);
}
// TODO: Check other image types.
return NULL;
}

117
src/codec/SkCodecPriv.h Normal file
View File

@ -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

View File

@ -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;
}

142
src/codec/SkCodec_libbmp.h Normal file
View File

@ -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;
};

View 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));
}
}

View File

@ -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);
}

View File

@ -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;
};

160
src/codec/SkMasks.cpp Normal file
View File

@ -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)
{}

81
src/codec/SkMasks.h Normal file
View File

@ -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;
};

View File

@ -5,105 +5,168 @@
* found in the LICENSE file.
*/
#include "SkCodecPriv.h"
#include "SkColorPriv.h"
#include "SkSwizzler.h"
#include "SkTemplates.h"
// index
#define A32_MASK_IN_PLACE (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT)
static bool swizzle_index_to_n32(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor ctable[]) {
SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
SkPMColor cc = A32_MASK_IN_PLACE;
for (int x = 0; x < width; x++) {
SkPMColor c = ctable[*src];
cc &= c;
dst[x] = c;
src += deltaSrc;
}
return cc != A32_MASK_IN_PLACE;
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;
}
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[]) {
// 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;
}
// kIndex
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++;
}
return COMPUTE_RESULT_ALPHA;
}
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;
INIT_RESULT_ALPHA;
for (int x = 0; x < width; x++) {
SkPMColor c = ctable[*src];
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);
}

View 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);
};