Refactor SkBmpCodec

I started working on indicating the native encoded formats
and things got really complicated for bmp.  I think starting
with this refactor may help a little, and I also think that
this is a good change by itself.

BUG=skia:4133
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1820283002

Review URL: https://codereview.chromium.org/1820283002
This commit is contained in:
msarett 2016-03-22 08:58:35 -07:00 committed by Commit bot
parent 4c9776b046
commit 1088db9234
2 changed files with 145 additions and 135 deletions

View File

@ -286,6 +286,18 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) {
switch (compression) {
case kNone_BmpCompressionMethod:
inputFormat = kStandard_BmpInputFormat;
// 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.
if (16 == bitsPerPixel) {
inputMasks.red = 0x7C00;
inputMasks.green = 0x03E0;
inputMasks.blue = 0x001F;
inputFormat = kBitMask_BmpInputFormat;
}
break;
case k8BitRLE_BmpCompressionMethod:
if (bitsPerPixel != 8) {
@ -331,6 +343,27 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) {
inputMasks.red = get_int(iBuffer.get(), 36);
inputMasks.green = get_int(iBuffer.get(), 40);
inputMasks.blue = get_int(iBuffer.get(), 44);
if (kInfoV2_BmpHeaderType == headerType ||
(kInfoV3_BmpHeaderType == headerType && !inIco)) {
break;
}
// V3+ 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. This is the
// case for almost all V3 images, so we ignore the alpha mask. For V4+
// images in kMask mode, we will use the alpha mask. Additionally, V3
// bmp-in-ico expect us to use the alpha mask.
//
// skbug.com/4116: We should perhaps also apply the alpha mask in kStandard
// mode. We just haven't seen any images that expect this
// behavior.
//
// 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);
inputMasks.alpha = get_int(iBuffer.get(), 48);
break;
case kOS2VX_BmpHeaderType:
// TODO: Decide if we intend to support this.
@ -366,101 +399,8 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) {
SkCodecPrintf("Error: invalid format for bitmap decoding.\n");
return false;
}
// 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. V3+ 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. This
// is the case for almost all V3 images, so we render these as opaque. For
// V4+ images in kMask mode, we will use the alpha mask.
//
// skbug.com/4116: We should perhaps also apply the alpha mask in kStandard
// mode. We just haven't seen any images that expect this
// behavior.
//
// Additionally, V3 bmp-in-ico may use the alpha mask.
SkAlphaType alphaType = kOpaque_SkAlphaType;
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);
inputMasks.alpha = get_int(iBuffer.get(), 48);
if (inputMasks.alpha != 0) {
alphaType = kUnpremul_SkAlphaType;
}
}
iBuffer.reset();
// Additionally, 32 bit bmp-in-icos use the alpha channel.
// FIXME (msarett): Don't all 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 ((inIco && 32 == bitsPerPixel) || (kRLE_BmpInputFormat == inputFormat)) {
alphaType = kUnpremul_SkAlphaType;
}
// Check for valid bits per pixel.
// At the same time, use this information to choose a suggested color type
// and to set default masks.
SkColorType colorType = kN32_SkColorType;
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_BmpInputFormat != inputFormat) {
inputMasks.red = 0x7C00;
inputMasks.green = 0x03E0;
inputMasks.blue = 0x001F;
inputFormat = kBitMask_BmpInputFormat;
}
break;
// We want to decode to kIndex_8 for input formats that are already
// designed in index format.
case 1:
case 2:
case 4:
case 8:
// 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_BmpInputFormat != inputFormat && !inIco) {
colorType = kIndex_8_SkColorType;
}
// Mask bmps must have 16, 24, or 32 bits per pixel.
if (kBitMask_BmpInputFormat == inputFormat) {
SkCodecPrintf("Error: invalid input value of bits per pixel for mask bmp.\n");
return false;
}
case 24:
case 32:
break;
default:
SkCodecPrintf("Error: invalid input value for bits per pixel.\n");
return false;
}
// Check that input bit masks are valid and create the masks object
SkAutoTDelete<SkMasks>
masks(SkMasks::CreateMasks(inputMasks, bitsPerPixel));
if (nullptr == masks) {
SkCodecPrintf("Error: invalid input masks.\n");
return false;
}
// Check for a valid number of total bytes when in RLE mode
if (totalBytes <= offset && kRLE_BmpInputFormat == inputFormat) {
SkCodecPrintf("Error: RLE requires valid input size.\n");
return false;
}
const size_t RLEBytes = totalBytes - offset;
// Calculate the number of bytes read so far
const uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes;
if (!inIco && offset < bytesRead) {
@ -471,63 +411,133 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) {
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 (!inIco && kBitMask_BmpInputFormat == inputFormat) {
if (stream->skip(offset - bytesRead) != offset - bytesRead) {
SkCodecPrintf("Error: unable to skip to image data.\n");
return false;
}
}
if (codecOut) {
// BMPs-in-ICOs contain an alpha mask after the image which means we
// cannot guarantee that an image is opaque, even if the bmp thinks
// it is.
bool isOpaque = kOpaque_SkAlphaType == alphaType;
if (inIco) {
alphaType = kUnpremul_SkAlphaType;
}
// Set the image info
const SkImageInfo& imageInfo = SkImageInfo::Make(width, height,
colorType, alphaType);
switch (inputFormat) {
case kStandard_BmpInputFormat: {
// BMPs-in-ICOs often contain an alpha mask after the image, which
// means we cannot guarantee that an image is opaque, even if the
// embedded bmp is opaque.
// We use |isOpaque| to indicate if the BMP itself is opaque, but
// still need to recommend kUnpremul when it is contained in an ICO.
SkColorType colorType = kN32_SkColorType;
SkAlphaType alphaType = inIco ? kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
bool isOpaque = true;
switch (bitsPerPixel) {
// Palette formats
case 1:
case 2:
case 4:
case 8:
// We cannot recommend a palette color type for ICOs because they
// may contain a transparency mask.
if (!inIco) {
colorType = kIndex_8_SkColorType;
}
break;
case 24:
case 32:
// 32-bit BMP-in-ICOs actually use the alpha channel in place of a
// transparency mask.
if (inIco) {
isOpaque = false;
}
break;
default:
SkCodecPrintf("Error: invalid input value for bits per pixel.\n");
return false;
}
// Return the codec
switch (inputFormat) {
case kStandard_BmpInputFormat:
if (codecOut) {
// We require streams to have a memory base for Bmp-in-Ico decodes.
SkASSERT(!inIco || nullptr != stream->getMemoryBase());
// Set the image info and create a codec.
const SkImageInfo imageInfo = SkImageInfo::Make(width, height, colorType,
alphaType);
*codecOut = new SkBmpStandardCodec(imageInfo, stream, bitsPerPixel, numColors,
bytesPerColor, offset - bytesRead, rowOrder, isOpaque, inIco);
return true;
case kBitMask_BmpInputFormat:
// Bmp-in-Ico must be standard mode
if (inIco) {
SkCodecPrintf("Error: Icos may not use bit mask format.\n");
}
return true;
}
case kBitMask_BmpInputFormat: {
// Bmp-in-Ico must be standard mode
if (inIco) {
SkCodecPrintf("Error: Icos may not use bit mask format.\n");
return false;
}
switch (bitsPerPixel) {
case 16:
case 24:
case 32:
break;
default:
SkCodecPrintf("Error: invalid input value for bits per pixel.\n");
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;
}
if (codecOut) {
// Check that input bit masks are valid and create the masks object
SkAutoTDelete<SkMasks> masks(SkMasks::CreateMasks(inputMasks, bitsPerPixel));
if (nullptr == masks) {
SkCodecPrintf("Error: invalid input masks.\n");
return false;
}
// Set the image info
SkAlphaType alphaType = masks->getAlphaMask() ? kUnpremul_SkAlphaType :
kOpaque_SkAlphaType;
const SkImageInfo imageInfo = SkImageInfo::Make(width, height, kN32_SkColorType,
alphaType);
*codecOut = new SkBmpMaskCodec(imageInfo, stream, bitsPerPixel, masks.release(),
rowOrder);
return true;
case kRLE_BmpInputFormat:
// Bmp-in-Ico must be standard mode
// When inIco is true, this line cannot be reached, since we
// require that RLE Bmps have a valid number of totalBytes, and
// Icos skip the header that contains totalBytes.
SkASSERT(!inIco);
}
return true;
}
case kRLE_BmpInputFormat: {
// We should not reach this point without a valid value of bitsPerPixel.
SkASSERT(4 == bitsPerPixel || 8 == bitsPerPixel || 24 == bitsPerPixel);
// Check for a valid number of total bytes when in RLE mode
if (totalBytes <= offset) {
SkCodecPrintf("Error: RLE requires valid input size.\n");
return false;
}
const size_t RLEBytes = totalBytes - offset;
// Bmp-in-Ico must be standard mode
// When inIco is true, this line cannot be reached, since we
// require that RLE Bmps have a valid number of totalBytes, and
// Icos skip the header that contains totalBytes.
SkASSERT(!inIco);
if (codecOut) {
// RLE inputs may skip pixels, leaving them as transparent. This
// is uncommon, but we cannot be certain that an RLE bmp will be
// opaque.
const SkImageInfo imageInfo = SkImageInfo::Make(width, height, kN32_SkColorType,
kUnpremul_SkAlphaType);
*codecOut = new SkBmpRLECodec(imageInfo, stream, bitsPerPixel, numColors,
bytesPerColor, offset - bytesRead, rowOrder, RLEBytes);
return true;
default:
SkASSERT(false);
return false;
}
return true;
}
default:
SkASSERT(false);
return false;
}
return true;
}
/*

View File

@ -228,7 +228,7 @@ int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo, void* dst, size_t
fSwizzler->swizzle(dstRow, fSrcBuffer.get());
}
if (fInIco) {
if (fInIco && fIsOpaque) {
const int startScanline = this->currScanline();
if (startScanline < 0) {
// We are not performing a scanline decode.