/* * 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 "src/codec/SkBmpCodec.h" #include #include "include/core/SkStream.h" #include "include/private/SkColorData.h" #include "src/codec/SkBmpMaskCodec.h" #include "src/codec/SkBmpRLECodec.h" #include "src/codec/SkBmpStandardCodec.h" #include "src/codec/SkCodecPriv.h" /* * Defines the version and type of the second bitmap header */ enum BmpHeaderType { kInfoV1_BmpHeaderType, kInfoV2_BmpHeaderType, kInfoV3_BmpHeaderType, kInfoV4_BmpHeaderType, kInfoV5_BmpHeaderType, kOS2V1_BmpHeaderType, kOS2VX_BmpHeaderType, kUnknown_BmpHeaderType }; /* * Possible bitmap compression types */ enum BmpCompressionMethod { kNone_BmpCompressionMethod = 0, k8BitRLE_BmpCompressionMethod = 1, k4BitRLE_BmpCompressionMethod = 2, kBitMasks_BmpCompressionMethod = 3, kJpeg_BmpCompressionMethod = 4, kPng_BmpCompressionMethod = 5, kAlphaBitMasks_BmpCompressionMethod = 6, kCMYK_BmpCompressionMethod = 11, kCMYK8BitRLE_BmpCompressionMethod = 12, kCMYK4BitRLE_BmpCompressionMethod = 13 }; /* * Used to define the input format of the bmp */ enum BmpInputFormat { kStandard_BmpInputFormat, kRLE_BmpInputFormat, kBitMask_BmpInputFormat, kUnknown_BmpInputFormat }; /* * Checks the start of the stream to see if the image is a bitmap */ bool SkBmpCodec::IsBmp(const void* buffer, size_t bytesRead) { // TODO: Support "IC", "PT", "CI", "CP", "BA" const char bmpSig[] = { 'B', 'M' }; return bytesRead >= sizeof(bmpSig) && !memcmp(buffer, bmpSig, sizeof(bmpSig)); } /* * Assumes IsBmp was called and returned true * Creates a bmp decoder * Reads enough of the stream to determine the image format */ std::unique_ptr SkBmpCodec::MakeFromStream(std::unique_ptr stream, Result* result) { return SkBmpCodec::MakeFromStream(std::move(stream), result, false); } /* * Creates a bmp decoder for a bmp embedded in ico * Reads enough of the stream to determine the image format */ std::unique_ptr SkBmpCodec::MakeFromIco(std::unique_ptr stream, Result* result) { return SkBmpCodec::MakeFromStream(std::move(stream), result, true); } // Header size constants static constexpr uint32_t kBmpHeaderBytes = 14; static constexpr uint32_t kBmpHeaderBytesPlusFour = kBmpHeaderBytes + 4; static constexpr uint32_t kBmpOS2V1Bytes = 12; static constexpr uint32_t kBmpOS2V2Bytes = 64; static constexpr uint32_t kBmpInfoBaseBytes = 16; static constexpr uint32_t kBmpInfoV1Bytes = 40; static constexpr uint32_t kBmpInfoV2Bytes = 52; static constexpr uint32_t kBmpInfoV3Bytes = 56; static constexpr uint32_t kBmpInfoV4Bytes = 108; static constexpr uint32_t kBmpInfoV5Bytes = 124; static constexpr uint32_t kBmpMaskBytes = 12; static BmpHeaderType get_header_type(size_t infoBytes) { if (infoBytes >= kBmpInfoBaseBytes) { // Check the version of the header switch (infoBytes) { case kBmpInfoV1Bytes: return kInfoV1_BmpHeaderType; case kBmpInfoV2Bytes: return kInfoV2_BmpHeaderType; case kBmpInfoV3Bytes: return kInfoV3_BmpHeaderType; case kBmpInfoV4Bytes: return kInfoV4_BmpHeaderType; case kBmpInfoV5Bytes: return kInfoV5_BmpHeaderType; case 16: case 20: case 24: case 28: case 32: case 36: case 42: case 46: case 48: case 60: case kBmpOS2V2Bytes: return kOS2VX_BmpHeaderType; default: SkCodecPrintf("Error: unknown bmp header format.\n"); return kUnknown_BmpHeaderType; } } if (infoBytes >= kBmpOS2V1Bytes) { // The OS2V1 is treated separately because it has a unique format return kOS2V1_BmpHeaderType; } else { // There are no valid bmp headers SkCodecPrintf("Error: second bitmap header size is invalid.\n"); return kUnknown_BmpHeaderType; } } SkCodec::Result SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, std::unique_ptr* codecOut) { // 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. uint32_t totalBytes; // The offset from the start of the file where the pixel data begins uint32_t offset; // The size of the second (info) header in bytes uint32_t infoBytes; // Bmps embedded in Icos skip the first Bmp header if (!inIco) { // Read the first header and the size of the second header uint8_t hBuffer[kBmpHeaderBytesPlusFour]; if (stream->read(hBuffer, kBmpHeaderBytesPlusFour) != kBmpHeaderBytesPlusFour) { SkCodecPrintf("Error: unable to read first bitmap header.\n"); return kIncompleteInput; } totalBytes = get_int(hBuffer, 2); offset = get_int(hBuffer, 10); if (offset < kBmpHeaderBytes + kBmpOS2V1Bytes) { SkCodecPrintf("Error: invalid starting location for pixel data\n"); return kInvalidInput; } // 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. infoBytes = get_int(hBuffer, 14); if (infoBytes < kBmpOS2V1Bytes) { SkCodecPrintf("Error: invalid second header size.\n"); return kInvalidInput; } } else { // This value is only used by RLE compression. Bmp in Ico files do not // use RLE. If the compression field is incorrectly signaled as RLE, // we will catch this and signal an error below. totalBytes = 0; // Bmps in Ico cannot specify an offset. We will always assume that // pixel data begins immediately after the color table. This value // will be corrected below. offset = 0; // Read the size of the second header uint8_t hBuffer[4]; if (stream->read(hBuffer, 4) != 4) { SkCodecPrintf("Error: unable to read size of second bitmap header.\n"); return kIncompleteInput; } infoBytes = get_int(hBuffer, 0); if (infoBytes < kBmpOS2V1Bytes) { SkCodecPrintf("Error: invalid second header size.\n"); return kInvalidInput; } } // Determine image information depending on second header format const BmpHeaderType headerType = get_header_type(infoBytes); if (kUnknown_BmpHeaderType == headerType) { return kInvalidInput; } // We already read the first four bytes of the info header to get the size const uint32_t infoBytesRemaining = infoBytes - 4; // Read the second header std::unique_ptr iBuffer(new uint8_t[infoBytesRemaining]); if (stream->read(iBuffer.get(), infoBytesRemaining) != infoBytesRemaining) { SkCodecPrintf("Error: unable to read second bitmap header.\n"); return kIncompleteInput; } // 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_BmpCompressionMethod; // 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; switch (headerType) { case kInfoV1_BmpHeaderType: case kInfoV2_BmpHeaderType: case kInfoV3_BmpHeaderType: case kInfoV4_BmpHeaderType: case kInfoV5_BmpHeaderType: case kOS2VX_BmpHeaderType: // 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; break; case kOS2V1_BmpHeaderType: // The OS2V1 is treated separately because it has a unique format width = (int) get_short(iBuffer.get(), 0); height = (int) get_short(iBuffer.get(), 2); bitsPerPixel = get_short(iBuffer.get(), 6); bytesPerColor = 3; break; case kUnknown_BmpHeaderType: // We'll exit above in this case. SkASSERT(false); return kInvalidInput; } // Check for valid dimensions from header SkCodec::SkScanlineOrder rowOrder = SkCodec::kBottomUp_SkScanlineOrder; if (height < 0) { // We can't negate INT32_MIN. if (height == INT32_MIN) { return kInvalidInput; } height = -height; rowOrder = SkCodec::kTopDown_SkScanlineOrder; } // The height field for bmp in ico is double the actual height because they // contain an XOR mask followed by an AND mask if (inIco) { height /= 2; } // Arbitrary maximum. Matches Chromium. constexpr int kMaxDim = 1 << 16; if (width <= 0 || height <= 0 || width >= kMaxDim || height >= kMaxDim) { SkCodecPrintf("Error: invalid bitmap dimensions.\n"); return kInvalidInput; } // Create mask struct SkMasks::InputMasks inputMasks; memset(&inputMasks, 0, sizeof(SkMasks::InputMasks)); // Determine the input compression format and set bit masks if necessary uint32_t maskBytes = 0; BmpInputFormat inputFormat = kUnknown_BmpInputFormat; 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) { SkCodecPrintf("Warning: correcting invalid bitmap format.\n"); bitsPerPixel = 8; } inputFormat = kRLE_BmpInputFormat; break; case k4BitRLE_BmpCompressionMethod: if (bitsPerPixel != 4) { SkCodecPrintf("Warning: correcting invalid bitmap format.\n"); bitsPerPixel = 4; } inputFormat = kRLE_BmpInputFormat; break; case kAlphaBitMasks_BmpCompressionMethod: case kBitMasks_BmpCompressionMethod: // Load the masks inputFormat = kBitMask_BmpInputFormat; switch (headerType) { case kInfoV1_BmpHeaderType: { // The V1 header stores the bit masks after the header uint8_t buffer[kBmpMaskBytes]; if (stream->read(buffer, kBmpMaskBytes) != kBmpMaskBytes) { SkCodecPrintf("Error: unable to read bit inputMasks.\n"); return kIncompleteInput; } maskBytes = kBmpMaskBytes; inputMasks.red = get_int(buffer, 0); inputMasks.green = get_int(buffer, 4); inputMasks.blue = get_int(buffer, 8); break; } case kInfoV2_BmpHeaderType: case kInfoV3_BmpHeaderType: case kInfoV4_BmpHeaderType: case kInfoV5_BmpHeaderType: // Header types are matched based on size. If the header // is V2+, we are guaranteed to be able to read at least // this size. SkASSERT(infoBytesRemaining >= 48); 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. // It is unsupported in the previous version and // in chromium. I have not come across a test case // that uses this format. SkCodecPrintf("Error: huffman format unsupported.\n"); return kUnimplemented; default: SkCodecPrintf("Error: invalid bmp bit masks header.\n"); return kInvalidInput; } break; case kJpeg_BmpCompressionMethod: if (24 == bitsPerPixel) { inputFormat = kRLE_BmpInputFormat; break; } [[fallthrough]]; case kPng_BmpCompressionMethod: // TODO: Decide if we intend to support this. // It is unsupported in the previous version and // in chromium. I think it is used mostly for printers. SkCodecPrintf("Error: compression format not supported.\n"); return kUnimplemented; case kCMYK_BmpCompressionMethod: case kCMYK8BitRLE_BmpCompressionMethod: case kCMYK4BitRLE_BmpCompressionMethod: // TODO: Same as above. SkCodecPrintf("Error: CMYK not supported for bitmap decoding.\n"); return kUnimplemented; default: SkCodecPrintf("Error: invalid format for bitmap decoding.\n"); return kInvalidInput; } iBuffer.reset(); // Calculate the number of bytes read so far const uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes; if (!inIco && offset < bytesRead) { // TODO (msarett): Do we really want to fail if the offset in the header is invalid? // Seems like we can just assume that the offset is zero and try to decode? // Maybe we don't want to try to decode corrupt images? SkCodecPrintf("Error: pixel data offset less than header size.\n"); return kInvalidInput; } switch (inputFormat) { case kStandard_BmpInputFormat: { // BMPs are generally opaque, however BMPs-in-ICOs may contain // a transparency mask after the image. Therefore, we mark the // alpha as kBinary if the BMP is contained in an ICO. // We use |isOpaque| to indicate if the BMP itself is opaque. SkEncodedInfo::Alpha alpha = inIco ? SkEncodedInfo::kBinary_Alpha : SkEncodedInfo::kOpaque_Alpha; bool isOpaque = true; SkEncodedInfo::Color color; uint8_t bitsPerComponent; switch (bitsPerPixel) { // Palette formats case 1: case 2: case 4: case 8: // In the case of ICO, kBGRA is actually the closest match, // since we will need to apply a transparency mask. if (inIco) { color = SkEncodedInfo::kBGRA_Color; bitsPerComponent = 8; } else { color = SkEncodedInfo::kPalette_Color; bitsPerComponent = (uint8_t) bitsPerPixel; } break; case 24: // In the case of ICO, kBGRA is actually the closest match, // since we will need to apply a transparency mask. color = inIco ? SkEncodedInfo::kBGRA_Color : SkEncodedInfo::kBGR_Color; bitsPerComponent = 8; break; case 32: // 32-bit BMP-in-ICOs actually use the alpha channel in place of a // transparency mask. if (inIco) { isOpaque = false; alpha = SkEncodedInfo::kUnpremul_Alpha; color = SkEncodedInfo::kBGRA_Color; } else { color = SkEncodedInfo::kBGRX_Color; } bitsPerComponent = 8; break; default: SkCodecPrintf("Error: invalid input value for bits per pixel.\n"); return kInvalidInput; } 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. auto info = SkEncodedInfo::Make(width, height, color, alpha, bitsPerComponent); *codecOut = std::make_unique(std::move(info), std::unique_ptr(stream), bitsPerPixel, numColors, bytesPerColor, offset - bytesRead, rowOrder, isOpaque, inIco); return static_cast(codecOut->get())->didCreateSrcBuffer() ? kSuccess : kInvalidInput; } return kSuccess; } case kBitMask_BmpInputFormat: { // Bmp-in-Ico must be standard mode if (inIco) { SkCodecPrintf("Error: Icos may not use bit mask format.\n"); return kInvalidInput; } switch (bitsPerPixel) { case 16: case 24: case 32: break; default: SkCodecPrintf("Error: invalid input value for bits per pixel.\n"); return kInvalidInput; } // 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 kIncompleteInput; } if (codecOut) { // Check that input bit masks are valid and create the masks object SkASSERT(bitsPerPixel % 8 == 0); std::unique_ptr masks(SkMasks::CreateMasks(inputMasks, bitsPerPixel/8)); if (nullptr == masks) { SkCodecPrintf("Error: invalid input masks.\n"); return kInvalidInput; } // Masked bmps are not a great fit for SkEncodedInfo, since they have // arbitrary component orderings and bits per component. Here we choose // somewhat reasonable values - it's ok that we don't match exactly // because SkBmpMaskCodec has its own mask swizzler anyway. SkEncodedInfo::Color color; SkEncodedInfo::Alpha alpha; if (masks->getAlphaMask()) { color = SkEncodedInfo::kBGRA_Color; alpha = SkEncodedInfo::kUnpremul_Alpha; } else { color = SkEncodedInfo::kBGR_Color; alpha = SkEncodedInfo::kOpaque_Alpha; } auto info = SkEncodedInfo::Make(width, height, color, alpha, 8); *codecOut = std::make_unique(std::move(info), std::unique_ptr(stream), bitsPerPixel, masks.release(), rowOrder); return static_cast(codecOut->get())->didCreateSrcBuffer() ? kSuccess : kInvalidInput; } return kSuccess; } 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 kInvalidInput; } // 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 or that we will be able to represent it with a palette. // For that reason, we always indicate that we are kBGRA. auto info = SkEncodedInfo::Make(width, height, SkEncodedInfo::kBGRA_Color, SkEncodedInfo::kBinary_Alpha, 8); *codecOut = std::make_unique(std::move(info), std::unique_ptr(stream), bitsPerPixel, numColors, bytesPerColor, offset - bytesRead, rowOrder); } return kSuccess; } default: SkASSERT(false); return kInvalidInput; } } /* * Creates a bmp decoder * Reads enough of the stream to determine the image format */ std::unique_ptr SkBmpCodec::MakeFromStream(std::unique_ptr stream, Result* result, bool inIco) { std::unique_ptr codec; *result = ReadHeader(stream.get(), inIco, &codec); if (codec) { // codec has taken ownership of stream, so we do not need to delete it. stream.release(); } return kSuccess == *result ? std::move(codec) : nullptr; } SkBmpCodec::SkBmpCodec(SkEncodedInfo&& info, std::unique_ptr stream, uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder) : INHERITED(std::move(info), kXformSrcColorFormat, std::move(stream)) , fBitsPerPixel(bitsPerPixel) , fRowOrder(rowOrder) , fSrcRowBytes(SkAlign4(compute_row_bytes(this->dimensions().width(), fBitsPerPixel))) , fXformBuffer(nullptr) {} bool SkBmpCodec::onRewind() { return SkBmpCodec::ReadHeader(this->stream(), this->inIco(), nullptr) == kSuccess; } int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const { if (SkCodec::kTopDown_SkScanlineOrder == fRowOrder) { return y; } SkASSERT(SkCodec::kBottomUp_SkScanlineOrder == fRowOrder); return height - y - 1; } SkCodec::Result SkBmpCodec::prepareToDecode(const SkImageInfo& dstInfo, const SkCodec::Options& options) { return this->onPrepareToDecode(dstInfo, options); } SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options& options) { return prepareToDecode(dstInfo, options); } int SkBmpCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { // Create a new image info representing the portion of the image to decode SkImageInfo rowInfo = this->dstInfo().makeWH(this->dstInfo().width(), count); // Decode the requested rows return this->decodeRows(rowInfo, dst, rowBytes, this->options()); } bool SkBmpCodec::skipRows(int count) { const size_t bytesToSkip = count * fSrcRowBytes; return this->stream()->skip(bytesToSkip) == bytesToSkip; } bool SkBmpCodec::onSkipScanlines(int count) { return this->skipRows(count); }