| /* | |
| * 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/codec/SkBmpDecoder.h" | |
| #include "include/core/SkData.h" | |
| #include "include/core/SkImageInfo.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkStream.h" | |
| #include "include/private/SkEncodedInfo.h" | |
| #include "include/private/base/SkAlign.h" | |
| #include "src/codec/SkBmpMaskCodec.h" | |
| #include "src/codec/SkBmpRLECodec.h" | |
| #include "src/codec/SkBmpStandardCodec.h" | |
| #include "src/codec/SkCodecPriv.h" | |
| #include "src/core/SkMasks.h" | |
| #include <cstring> | |
| #include <memory> | |
| #include <utility> | |
| /* | |
| * 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<SkCodec> SkBmpCodec::MakeFromStream(std::unique_ptr<SkStream> 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<SkCodec> SkBmpCodec::MakeFromIco(std::unique_ptr<SkStream> 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<SkCodec>* 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 = SkCodecPriv::UnsafeGetInt(hBuffer, 2); | |
| offset = SkCodecPriv::UnsafeGetInt(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 = SkCodecPriv::UnsafeGetInt(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 = SkCodecPriv::UnsafeGetInt(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<uint8_t[]> 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 = SkCodecPriv::UnsafeGetInt(iBuffer.get(), 0); | |
| height = SkCodecPriv::UnsafeGetInt(iBuffer.get(), 4); | |
| bitsPerPixel = SkCodecPriv::UnsafeGetShort(iBuffer.get(), 10); | |
| // Some versions do not have these fields, so we check before | |
| // overwriting the default value. | |
| if (infoBytesRemaining >= 16) { | |
| compression = SkCodecPriv::UnsafeGetInt(iBuffer.get(), 12); | |
| if (infoBytesRemaining >= 32) { | |
| numColors = SkCodecPriv::UnsafeGetInt(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)SkCodecPriv::UnsafeGetShort(iBuffer.get(), 0); | |
| height = (int)SkCodecPriv::UnsafeGetShort(iBuffer.get(), 2); | |
| bitsPerPixel = SkCodecPriv::UnsafeGetShort(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 = SkCodecPriv::UnsafeGetInt(buffer, 0); | |
| inputMasks.green = SkCodecPriv::UnsafeGetInt(buffer, 4); | |
| inputMasks.blue = SkCodecPriv::UnsafeGetInt(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 = SkCodecPriv::UnsafeGetInt(iBuffer.get(), 36); | |
| inputMasks.green = SkCodecPriv::UnsafeGetInt(iBuffer.get(), 40); | |
| inputMasks.blue = SkCodecPriv::UnsafeGetInt(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/40035265: 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 = SkCodecPriv::UnsafeGetInt(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<SkBmpStandardCodec>(std::move(info), | |
| std::unique_ptr<SkStream>(stream), | |
| bitsPerPixel, numColors, bytesPerColor, | |
| offset - bytesRead, rowOrder, isOpaque, | |
| inIco); | |
| return static_cast<SkBmpStandardCodec*>(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<SkMasks> 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<SkBmpMaskCodec>(std::move(info), | |
| std::unique_ptr<SkStream>(stream), bitsPerPixel, | |
| masks.release(), rowOrder); | |
| return static_cast<SkBmpMaskCodec*>(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<SkBmpRLECodec>(std::move(info), | |
| std::unique_ptr<SkStream>(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<SkCodec> SkBmpCodec::MakeFromStream(std::unique_ptr<SkStream> stream, | |
| Result* result, bool inIco) { | |
| SkASSERT(result); | |
| if (!stream) { | |
| *result = SkCodec::kInvalidInput; | |
| return nullptr; | |
| } | |
| std::unique_ptr<SkCodec> 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<SkStream> stream, | |
| uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder) | |
| : INHERITED(std::move(info), kXformSrcColorFormat, std::move(stream)) | |
| , fBitsPerPixel(bitsPerPixel) | |
| , fRowOrder(rowOrder) | |
| , fSrcRowBytes(SkAlign4(SkCodecPriv::ComputeRowBytes(this->dimensions().width(), fBitsPerPixel))) | |
| , fXformBuffer(nullptr) | |
| {} | |
| bool SkBmpCodec::onRewind() { | |
| if (!this->rewindStream()) { | |
| return false; | |
| } | |
| 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); | |
| } | |
| namespace SkBmpDecoder { | |
| bool IsBmp(const void* data, size_t len) { | |
| return SkBmpCodec::IsBmp(data, len); | |
| } | |
| std::unique_ptr<SkCodec> Decode(std::unique_ptr<SkStream> stream, | |
| SkCodec::Result* outResult, | |
| SkCodecs::DecodeContext) { | |
| SkCodec::Result resultStorage; | |
| if (!outResult) { | |
| outResult = &resultStorage; | |
| } | |
| return SkBmpCodec::MakeFromStream(std::move(stream), outResult); | |
| } | |
| std::unique_ptr<SkCodec> Decode(sk_sp<SkData> data, | |
| SkCodec::Result* outResult, | |
| SkCodecs::DecodeContext) { | |
| if (!data) { | |
| if (outResult) { | |
| *outResult = SkCodec::kInvalidInput; | |
| } | |
| return nullptr; | |
| } | |
| return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr); | |
| } | |
| } // namespace SkBmpDecoder |