Index: src/codec/SkBmpCodec.cpp |
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp |
index e327f790187fc434aad4317b78e1f9650c40d294..ad6f0ddc4d5312b6e28e19ccb8b0655325c63bf9 100644 |
--- a/src/codec/SkBmpCodec.cpp |
+++ b/src/codec/SkBmpCodec.cpp |
@@ -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; |
} |
/* |