2015-10-13 16:08:06 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-05-21 04:12:37 -07:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2015-04-05 16:22:00 -07:00
|
|
|
#ifndef mozilla_image_decoders_nsBMPDecoder_h
|
|
|
|
#define mozilla_image_decoders_nsBMPDecoder_h
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2015-10-15 15:43:25 -07:00
|
|
|
#include "BMPHeaders.h"
|
2014-11-14 09:59:00 -08:00
|
|
|
#include "Decoder.h"
|
|
|
|
#include "gfxColor.h"
|
Bug 1204394 (part 1) - Using StreamingLexer in the BMP decoder. r=seth.
This patch is a major overhaul of nsBMPDecoder.
The patch improves the code in the following ways.
- It converts nsBMPDecoder to use StreamingLexer, which makes it much easier to
read.
- It adds a detailed comment about the BMP format at the top of
nsBMPDecoder.cpp.
- It fixes lots of inconsistent indenting.
- It moves |bihsize| from |mBFH| to |mBIH| to match the file format and common
sense. The avoids the need for the confusing LENGTH/INTERNAL_LENGTH
distinction.
- It renames most of the types in BMPFileHeader.h, so they have better names,
in StudlyCaps form, and within the new |bmp| namespace.
- It removes the BMP_HEADER_LENGTH struct and inlines its values directly into
the two places they were used.
- It removes the MOZ_LOG logging done on some of the failure cases. (Most
failure cases lacked logging so why bother with some?)
- It removes over 200 lines of code, despite the addition of the big format
comment.
The patch changes the way BMPs are decoded as follows.
- It adds stricter testing of the InfoHeader length, rejecting files with bad
values.
- It moves all header sanity checking that can lead to file rejection into the
metadata decode phase. (Previously, bpp/compression consistency checking did
not occur during a metadata decode.)
- It removes BMPINFOHEADER::ALPHABITFIELDS, which was (a) a weird WinCE-only
thing, and (b) we didn't actually allow it, and (c) we used the value 4
instead of 6(!).
- It rejects the previously-accepted compression==RLE4 && bpp=1 combination
because it doesn't make sense.
- It removes a fudge in RLE absolute mode handling that permitted one pixel too
many in a row but only if the row's width was odd(!)
- It now rejects a file with a negative gap between the color table and the
pixel data.
The patch leaves the following problems unaddressed.
- If bpp==32 we totally ignore compression==BITFIELDS and treat it like
compression=RGB.
- Transparency as specified in WinBMPv{4,5} isn't handled at all.
These will be fixed in follow-ups.
All these changes affect (for the better) the results of the following tests
that will be added in part 2:
- g/pal8v4.bmp
- g/pal8v5.bmp
- q/pal8os2sp.bmp
- q/pal8os2v2.bmp
- q/pal8os2v2-16.bmp
- b/badheadersize.bmp
- b/badpalettesize.bmp
- b/badrle.bmp
2015-10-08 22:47:56 -07:00
|
|
|
#include "StreamingLexer.h"
|
2015-10-27 07:47:51 -07:00
|
|
|
#include "mozilla/UniquePtr.h"
|
2010-08-22 19:30:46 -07:00
|
|
|
|
|
|
|
namespace mozilla {
|
2012-01-06 08:02:27 -08:00
|
|
|
namespace image {
|
2010-08-12 12:58:25 -07:00
|
|
|
|
2015-10-13 16:43:18 -07:00
|
|
|
namespace bmp {
|
|
|
|
|
2015-10-15 15:43:25 -07:00
|
|
|
/// This struct contains the fields from the file header and info header that
|
|
|
|
/// we use during decoding. (Excluding bitfields fields, which are kept in
|
|
|
|
/// BitFields.)
|
|
|
|
struct Header {
|
|
|
|
uint32_t mDataOffset; // Offset to raster data.
|
|
|
|
uint32_t mBIHSize; // Header size.
|
|
|
|
int32_t mWidth; // Image width.
|
|
|
|
int32_t mHeight; // Image height.
|
|
|
|
uint16_t mBpp; // Bits per pixel.
|
|
|
|
uint32_t mCompression; // See struct Compression for valid values.
|
|
|
|
uint32_t mImageSize; // (compressed) image size. Can be 0 if
|
|
|
|
// mCompression==0.
|
|
|
|
uint32_t mNumColors; // Used colors.
|
|
|
|
|
|
|
|
Header()
|
|
|
|
: mDataOffset(0)
|
|
|
|
, mBIHSize(0)
|
|
|
|
, mWidth(0)
|
|
|
|
, mHeight(0)
|
|
|
|
, mBpp(0)
|
|
|
|
, mCompression(0)
|
|
|
|
, mImageSize(0)
|
|
|
|
, mNumColors(0)
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2015-10-13 16:43:18 -07:00
|
|
|
/// An entry in the color table.
|
|
|
|
struct ColorTableEntry {
|
|
|
|
uint8_t mRed;
|
|
|
|
uint8_t mGreen;
|
|
|
|
uint8_t mBlue;
|
|
|
|
};
|
|
|
|
|
2015-10-13 20:01:02 -07:00
|
|
|
/// All the color-related bitfields for 16bpp and 32bpp images. We use this
|
|
|
|
/// even for older format BMPs that don't have explicit bitfields.
|
|
|
|
class BitFields {
|
|
|
|
class Value {
|
|
|
|
friend class BitFields;
|
|
|
|
|
|
|
|
uint32_t mMask; // The mask for the value.
|
|
|
|
uint8_t mRightShift; // The amount to right-shift after masking.
|
|
|
|
uint8_t mBitWidth; // The width (in bits) of the value.
|
|
|
|
|
|
|
|
/// Sets the mask (and thus the right-shift and bit-width as well).
|
|
|
|
void Set(uint32_t aMask);
|
|
|
|
|
|
|
|
public:
|
2015-10-13 21:20:10 -07:00
|
|
|
Value()
|
|
|
|
{
|
|
|
|
mMask = 0;
|
|
|
|
mRightShift = 0;
|
|
|
|
mBitWidth = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true if this channel is used. Only used for alpha.
|
|
|
|
bool IsPresent() const { return mMask != 0x0; }
|
|
|
|
|
2015-10-13 20:01:02 -07:00
|
|
|
/// Extracts the single color value from the multi-color value.
|
|
|
|
uint8_t Get(uint32_t aVal) const;
|
|
|
|
|
2015-10-13 21:20:10 -07:00
|
|
|
/// Like Get(), but specially for alpha.
|
|
|
|
uint8_t GetAlpha(uint32_t aVal, bool& aHasAlphaOut) const;
|
|
|
|
|
|
|
|
/// Specialized versions of Get() for when the bit-width is 5 or 8.
|
|
|
|
/// (They will assert if called and the bit-width is not 5 or 8.)
|
2015-10-13 20:01:02 -07:00
|
|
|
uint8_t Get5(uint32_t aVal) const;
|
2015-10-13 21:20:10 -07:00
|
|
|
uint8_t Get8(uint32_t aVal) const;
|
2015-10-13 20:01:02 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
public:
|
|
|
|
/// The individual color channels.
|
|
|
|
Value mRed;
|
|
|
|
Value mGreen;
|
|
|
|
Value mBlue;
|
2015-10-13 21:20:10 -07:00
|
|
|
Value mAlpha;
|
2015-10-13 20:01:02 -07:00
|
|
|
|
|
|
|
/// Set bitfields to the standard 5-5-5 16bpp values.
|
|
|
|
void SetR5G5B5();
|
|
|
|
|
2015-10-13 21:20:10 -07:00
|
|
|
/// Set bitfields to the standard 8-8-8 32bpp values.
|
|
|
|
void SetR8G8B8();
|
|
|
|
|
2015-10-13 20:01:02 -07:00
|
|
|
/// Test if bitfields have the standard 5-5-5 16bpp values.
|
|
|
|
bool IsR5G5B5() const;
|
|
|
|
|
2015-10-13 21:20:10 -07:00
|
|
|
/// Test if bitfields have the standard 8-8-8 32bpp values.
|
|
|
|
bool IsR8G8B8() const;
|
|
|
|
|
|
|
|
/// Read the bitfields from a header. The reading of the alpha mask is
|
|
|
|
/// optional.
|
|
|
|
void ReadFromHeader(const char* aData, bool aReadAlpha);
|
2015-10-13 20:01:02 -07:00
|
|
|
|
|
|
|
/// Length of the bitfields structure in the BMP file.
|
2015-10-13 16:43:18 -07:00
|
|
|
static const size_t LENGTH = 12;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace bmp
|
|
|
|
|
2010-08-13 21:09:49 -07:00
|
|
|
class RasterImage;
|
|
|
|
|
2015-10-13 16:08:06 -07:00
|
|
|
/// Decoder for BMP-Files, as used by Windows and OS/2.
|
2014-11-14 09:59:00 -08:00
|
|
|
|
2010-08-22 19:30:46 -07:00
|
|
|
class nsBMPDecoder : public Decoder
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
public:
|
2015-10-13 16:08:06 -07:00
|
|
|
~nsBMPDecoder();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2015-10-13 16:08:06 -07:00
|
|
|
/// Obtains the internal output image buffer.
|
2015-10-15 17:38:32 -07:00
|
|
|
uint32_t* GetImageData() { return reinterpret_cast<uint32_t*>(mImageData); }
|
|
|
|
|
|
|
|
/// Obtains the length of the internal output image buffer.
|
2015-10-13 16:08:06 -07:00
|
|
|
size_t GetImageDataLength() const { return mImageDataLength; }
|
2014-11-14 09:59:00 -08:00
|
|
|
|
2015-10-13 16:08:06 -07:00
|
|
|
/// Obtains the size of the compressed image resource.
|
|
|
|
int32_t GetCompressedImageSize() const;
|
2014-11-14 09:59:00 -08:00
|
|
|
|
2015-10-15 15:43:31 -07:00
|
|
|
/// Mark this BMP as being within an ICO file. Only used for testing purposes
|
|
|
|
/// because the ICO-specific constructor does this marking automatically.
|
2015-10-13 21:20:10 -07:00
|
|
|
void SetIsWithinICO() { mIsWithinICO = true; }
|
|
|
|
|
|
|
|
/// Did the BMP file have alpha data of any kind? (Only use this after the
|
|
|
|
/// bitmap has been fully decoded.)
|
|
|
|
bool HasTransparency() const { return mDoesHaveTransparency; }
|
2015-09-08 22:07:09 -07:00
|
|
|
|
2015-10-13 21:20:10 -07:00
|
|
|
/// Force transparency from outside. (Used by the ICO decoder.)
|
|
|
|
void SetHasTransparency()
|
|
|
|
{
|
|
|
|
mMayHaveTransparency = true;
|
|
|
|
mDoesHaveTransparency = true;
|
|
|
|
}
|
2011-08-25 13:09:01 -07:00
|
|
|
|
2015-10-13 16:08:06 -07:00
|
|
|
virtual void WriteInternal(const char* aBuffer,
|
|
|
|
uint32_t aCount) override;
|
|
|
|
virtual void FinishInternal() override;
|
2010-08-22 19:30:46 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
private:
|
2015-10-13 16:08:06 -07:00
|
|
|
friend class DecoderFactory;
|
|
|
|
friend class nsICODecoder;
|
|
|
|
|
|
|
|
enum class State {
|
|
|
|
FILE_HEADER,
|
|
|
|
INFO_HEADER_SIZE,
|
|
|
|
INFO_HEADER_REST,
|
|
|
|
BITFIELDS,
|
|
|
|
COLOR_TABLE,
|
|
|
|
GAP,
|
|
|
|
PIXEL_ROW,
|
|
|
|
RLE_SEGMENT,
|
|
|
|
RLE_DELTA,
|
2015-10-28 01:30:20 -07:00
|
|
|
RLE_ABSOLUTE
|
2015-10-13 16:08:06 -07:00
|
|
|
};
|
|
|
|
|
2015-10-15 15:43:31 -07:00
|
|
|
// This is the constructor used by DecoderFactory.
|
|
|
|
explicit nsBMPDecoder(RasterImage* aImage);
|
|
|
|
|
|
|
|
// This is the constructor used by nsICODecoder.
|
|
|
|
// XXX(seth): nsICODecoder is temporarily an exception to the rule that
|
|
|
|
// decoders should only be instantiated via DecoderFactory.
|
|
|
|
nsBMPDecoder(RasterImage* aImage, uint32_t aDataOffset);
|
|
|
|
|
|
|
|
// Helper constructor called by the other two.
|
|
|
|
nsBMPDecoder(RasterImage* aImage, State aState, size_t aLength);
|
|
|
|
|
2015-10-15 17:38:32 -07:00
|
|
|
int32_t AbsoluteHeight() const { return abs(mH.mHeight); }
|
|
|
|
|
2015-10-15 15:43:31 -07:00
|
|
|
uint32_t* RowBuffer();
|
|
|
|
|
|
|
|
void FinishRow();
|
|
|
|
|
2015-10-13 16:08:06 -07:00
|
|
|
LexerTransition<State> ReadFileHeader(const char* aData, size_t aLength);
|
|
|
|
LexerTransition<State> ReadInfoHeaderSize(const char* aData, size_t aLength);
|
|
|
|
LexerTransition<State> ReadInfoHeaderRest(const char* aData, size_t aLength);
|
|
|
|
LexerTransition<State> ReadBitfields(const char* aData, size_t aLength);
|
|
|
|
LexerTransition<State> ReadColorTable(const char* aData, size_t aLength);
|
|
|
|
LexerTransition<State> SkipGap();
|
|
|
|
LexerTransition<State> ReadPixelRow(const char* aData);
|
|
|
|
LexerTransition<State> ReadRLESegment(const char* aData);
|
|
|
|
LexerTransition<State> ReadRLEDelta(const char* aData);
|
|
|
|
LexerTransition<State> ReadRLEAbsolute(const char* aData, size_t aLength);
|
|
|
|
|
|
|
|
StreamingLexer<State> mLexer;
|
|
|
|
|
2015-10-15 15:43:25 -07:00
|
|
|
bmp::Header mH;
|
2015-10-13 16:08:06 -07:00
|
|
|
|
2015-10-13 21:20:10 -07:00
|
|
|
// If the BMP is within an ICO file our treatment of it differs slightly.
|
|
|
|
bool mIsWithinICO;
|
|
|
|
|
2015-10-13 16:08:06 -07:00
|
|
|
bmp::BitFields mBitFields;
|
|
|
|
|
2015-10-13 20:01:24 -07:00
|
|
|
// Might the image have transparency? Determined from the headers during
|
|
|
|
// metadata decode. (Does not guarantee the image actually has transparency.)
|
|
|
|
bool mMayHaveTransparency;
|
|
|
|
|
2015-10-13 21:20:10 -07:00
|
|
|
// Does the image have transparency? Determined during full decoding, so only
|
|
|
|
// use this after that has been completed.
|
|
|
|
bool mDoesHaveTransparency;
|
|
|
|
|
2015-10-13 16:08:06 -07:00
|
|
|
uint32_t mNumColors; // The number of used colors, i.e. the number of
|
|
|
|
// entries in mColors, if it's present.
|
2015-10-27 07:47:51 -07:00
|
|
|
UniquePtr<bmp::ColorTableEntry[]> mColors; // The color table, if it's present.
|
2015-10-13 16:08:06 -07:00
|
|
|
uint32_t mBytesPerColor; // 3 or 4, depending on the format
|
|
|
|
|
|
|
|
// The number of bytes prior to the optional gap that have been read. This
|
|
|
|
// is used to find the start of the pixel data.
|
|
|
|
uint32_t mPreGapLength;
|
|
|
|
|
|
|
|
uint32_t mPixelRowSize; // The number of bytes per pixel row.
|
|
|
|
|
|
|
|
int32_t mCurrentRow; // Index of the row of the image that's currently
|
|
|
|
// being decoded: [height,1].
|
2015-11-15 20:31:07 -08:00
|
|
|
int32_t mCurrentPos; // Index into the current line. Used when
|
|
|
|
// doing RLE decoding and when filling in pixels
|
|
|
|
// for truncated files.
|
2015-10-13 16:08:06 -07:00
|
|
|
|
|
|
|
// Only used in RLE_ABSOLUTE state: the number of pixels to read.
|
|
|
|
uint32_t mAbsoluteModeNumPixels;
|
2007-03-22 10:30:00 -07:00
|
|
|
};
|
|
|
|
|
2012-01-06 08:02:27 -08:00
|
|
|
} // namespace image
|
2010-08-22 19:30:46 -07:00
|
|
|
} // namespace mozilla
|
|
|
|
|
2015-04-05 16:22:00 -07:00
|
|
|
#endif // mozilla_image_decoders_nsBMPDecoder_h
|