diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp index e327f79018..ad6f0ddc4d 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 - 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 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; } /* diff --git a/src/codec/SkBmpStandardCodec.cpp b/src/codec/SkBmpStandardCodec.cpp index 39d357be8b..80a989ae4d 100644 --- a/src/codec/SkBmpStandardCodec.cpp +++ b/src/codec/SkBmpStandardCodec.cpp @@ -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.