Commit 9f66a292 authored by Peter Kasting's avatar Peter Kasting Committed by Commit Bot

Support (and add more documentation on) a variety of uncommon BMP cases.

* "Bitmap Array" files (OS/2)
* BITMAPV2HEADER info headers (Photoshop?)
* BITMAPV3HEADER info headers (Photoshop?)
* BI_ALPHABITFIELDS compression (Windows CE)
* 2bpp palettes (Windows CE)
* Top-down bitmaps with RLE compression (illegal per spec, but supported by
  Windows and Firefox)
* Truncated color palettes

This increases the number of testcases Chrome can render on
http://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html .

Bug: 552274
Change-Id: I4c86b8328d326992a2ebca2e5178727aeab3afc0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1769524
Auto-Submit: Peter Kasting <pkasting@chromium.org>
Commit-Queue: Leon Scroggins <scroggo@chromium.org>
Reviewed-by: default avatarLeon Scroggins <scroggo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#690377}
parent 5b15998c
...@@ -98,21 +98,17 @@ bool BMPImageDecoder::DecodeHelper(bool only_size) { ...@@ -98,21 +98,17 @@ bool BMPImageDecoder::DecodeHelper(bool only_size) {
bool BMPImageDecoder::ProcessFileHeader(size_t& img_data_offset) { bool BMPImageDecoder::ProcessFileHeader(size_t& img_data_offset) {
// Read file header. // Read file header.
DCHECK(!decoded_offset_); DCHECK(!decoded_offset_);
if (data_->size() < kSizeOfFileHeader)
return false;
char buffer[kSizeOfFileHeader];
FastSharedBufferReader fast_reader(data_); FastSharedBufferReader fast_reader(data_);
const char* file_header = char buffer[kSizeOfFileHeader];
fast_reader.GetConsecutiveData(0, kSizeOfFileHeader, buffer); const char* file_header;
const uint16_t file_type = uint16_t file_type;
(file_header[0] << 8) | static_cast<uint8_t>(file_header[1]); if (!GetFileType(fast_reader, buffer, file_header, file_type))
img_data_offset = BMPImageReader::ReadUint32(&file_header[10]); return false;
decoded_offset_ = kSizeOfFileHeader;
// See if this is a bitmap filetype we understand. // See if this is a bitmap filetype we understand.
enum { enum {
BMAP = 0x424D, // "BM" BMAP = 0x424D, // "BM"
BITMAPARRAY = 0x4241, // "BA"
// The following additional OS/2 2.x header values (see // The following additional OS/2 2.x header values (see
// http://www.fileformat.info/format/os2bmp/egff.htm ) aren't widely // http://www.fileformat.info/format/os2bmp/egff.htm ) aren't widely
// decoded, and are unlikely to be in much use. // decoded, and are unlikely to be in much use.
...@@ -121,10 +117,32 @@ bool BMPImageDecoder::ProcessFileHeader(size_t& img_data_offset) { ...@@ -121,10 +117,32 @@ bool BMPImageDecoder::ProcessFileHeader(size_t& img_data_offset) {
POINTER = 0x5054, // "PT" POINTER = 0x5054, // "PT"
COLORICON = 0x4349, // "CI" COLORICON = 0x4349, // "CI"
COLORPOINTER = 0x4350, // "CP" COLORPOINTER = 0x4350, // "CP"
BITMAPARRAY = 0x4241, // "BA"
*/ */
}; };
return (file_type == BMAP) || SetFailed(); if (file_type == BITMAPARRAY) {
// Skip initial 14-byte header, try to read the first entry as a BMAP.
decoded_offset_ += kSizeOfFileHeader;
if (!GetFileType(fast_reader, buffer, file_header, file_type))
return false;
}
if (file_type != BMAP)
return SetFailed();
img_data_offset = BMPImageReader::ReadUint32(&file_header[10]);
decoded_offset_ += kSizeOfFileHeader;
return true;
}
bool BMPImageDecoder::GetFileType(const FastSharedBufferReader& fast_reader,
char* buffer,
const char*& file_header,
uint16_t& file_type) const {
if (data_->size() - decoded_offset_ < kSizeOfFileHeader)
return false;
file_header = fast_reader.GetConsecutiveData(decoded_offset_,
kSizeOfFileHeader, buffer);
file_type = (file_header[0] << 8) | static_cast<uint8_t>(file_header[1]);
return true;
} }
} // namespace blink } // namespace blink
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
namespace blink { namespace blink {
class BMPImageReader; class BMPImageReader;
class FastSharedBufferReader;
// This class decodes the BMP image format. // This class decodes the BMP image format.
class PLATFORM_EXPORT BMPImageDecoder final : public ImageDecoder { class PLATFORM_EXPORT BMPImageDecoder final : public ImageDecoder {
...@@ -72,6 +73,14 @@ class PLATFORM_EXPORT BMPImageDecoder final : public ImageDecoder { ...@@ -72,6 +73,14 @@ class PLATFORM_EXPORT BMPImageDecoder final : public ImageDecoder {
// file header could be decoded. // file header could be decoded.
bool ProcessFileHeader(size_t& img_data_offset); bool ProcessFileHeader(size_t& img_data_offset);
// Uses |fast_reader| and |buffer| to read the file header into |file_header|.
// Computes |file_type| from the file header. Returns whether there was
// sufficient data available to read the header.
bool GetFileType(const FastSharedBufferReader& fast_reader,
char* buffer,
const char*& file_header,
uint16_t& file_type) const;
// An index into |data_| representing how much we've already decoded. // An index into |data_| representing how much we've already decoded.
// Note that this only tracks data _this_ class decodes; once the // Note that this only tracks data _this_ class decodes; once the
// BMPImageReader takes over this will not be updated further. // BMPImageReader takes over this will not be updated further.
......
...@@ -96,6 +96,7 @@ class PLATFORM_EXPORT BMPImageReader final { ...@@ -96,6 +96,7 @@ class PLATFORM_EXPORT BMPImageReader final {
BITFIELDS = 3, BITFIELDS = 3,
JPEG = 4, JPEG = 4,
PNG = 5, PNG = 5,
ALPHABITFIELDS = 6, // Windows CE only
// OS/2 2.x-only // OS/2 2.x-only
HUFFMAN1D, // Stored in file as 3 HUFFMAN1D, // Stored in file as 3
RLE24, // Stored in file as 4 RLE24, // Stored in file as 4
...@@ -154,16 +155,29 @@ class PLATFORM_EXPORT BMPImageReader final { ...@@ -154,16 +155,29 @@ class PLATFORM_EXPORT BMPImageReader final {
// of header values from the byte stream. Returns false on error. // of header values from the byte stream. Returns false on error.
bool ReadInfoHeader(); bool ReadInfoHeader();
// Returns true if this is a Windows V4+ BMP. // Returns true if this BMP has an alpha mask in the info header
inline bool IsWindowsV4Plus() const { // (BITMAPV3HEADER+). See comments in ReadInfoHeader() for more.
// Windows V4 info header is 108 bytes. V5 is 124 bytes. inline bool HasAlphaMaskInHeader() const {
return (info_header_.bi_size == 108) || (info_header_.bi_size == 124); // BITMAPV3HEADER is 56 bytes; this is also a valid OS/2 2.x header size, so
// exclude that case.
return (info_header_.bi_size == 56 && !is_os22x_) || // BITMAPV3HEADER
(info_header_.bi_size == 108) || // BITMAPV4HEADER
(info_header_.bi_size == 124); // BITMAPV5HEADER
}
// Returns true if this BMP has RGB masks in the info header
// (BITMAPV2HEADER+). See comments in ReadInfoHeader() for more.
inline bool HasRGBMasksInHeader() const {
// BITMAPV2HEADER is 52 bytes; this is also a valid OS/2 2.x header size, so
// exclude that case.
return (info_header_.bi_size == 52 && !is_os22x_) || // BITMAPV2HEADER
HasAlphaMaskInHeader(); // BITMAPV3HEADER+
} }
// Returns false if consistency errors are found in the info header. // Returns false if consistency errors are found in the info header.
bool IsInfoHeaderValid() const; bool IsInfoHeaderValid() const;
// For BI_BITFIELDS images, initializes the bit_masks_[] and // For BI_[ALPHA]BITFIELDS images, initializes the bit_masks_[] and
// bit_offsets_[] arrays. ProcessInfoHeader() will initialize these for // bit_offsets_[] arrays. ProcessInfoHeader() will initialize these for
// other compression types where needed. // other compression types where needed.
bool ProcessBitmasks(); bool ProcessBitmasks();
......
...@@ -62,7 +62,7 @@ inline bool MatchesCURSignature(const char* contents) { ...@@ -62,7 +62,7 @@ inline bool MatchesCURSignature(const char* contents) {
} }
inline bool MatchesBMPSignature(const char* contents) { inline bool MatchesBMPSignature(const char* contents) {
return !memcmp(contents, "BM", 2); return !memcmp(contents, "BM", 2) || !memcmp(contents, "BA", 2);
} }
static constexpr size_t kLongestSignatureLength = sizeof("RIFF????WEBPVP") - 1; static constexpr size_t kLongestSignatureLength = sizeof("RIFF????WEBPVP") - 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment