Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / image / decoders / nsBMPDecoder.cpp
blob98d2814175812b8561ab69e59cd442b062a2362f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // This is a cross-platform BMP Decoder, which should work everywhere,
8 // including big-endian machines like the PowerPC.
9 //
10 // BMP is a format that has been extended multiple times. To understand the
11 // decoder you need to understand this history. The summary of the history
12 // below was determined from the following documents.
14 // - http://www.fileformat.info/format/bmp/egff.htm
15 // - http://www.fileformat.info/format/os2bmp/egff.htm
16 // - http://fileformats.archiveteam.org/wiki/BMP
17 // - http://fileformats.archiveteam.org/wiki/OS/2_BMP
18 // - https://en.wikipedia.org/wiki/BMP_file_format
19 // - https://upload.wikimedia.org/wikipedia/commons/c/c4/BMPfileFormat.png
21 // WINDOWS VERSIONS OF THE BMP FORMAT
22 // ----------------------------------
23 // WinBMPv1.
24 // - This version is no longer used and can be ignored.
26 // WinBMPv2.
27 // - First is a 14 byte file header that includes: the magic number ("BM"),
28 // file size, and offset to the pixel data (|mDataOffset|).
29 // - Next is a 12 byte info header which includes: the info header size
30 // (mBIHSize), width, height, number of color planes, and bits-per-pixel
31 // (|mBpp|) which must be 1, 4, 8 or 24.
32 // - Next is the semi-optional color table, which has length 2^|mBpp| and has 3
33 // bytes per value (BGR). The color table is required if |mBpp| is 1, 4, or 8.
34 // - Next is an optional gap.
35 // - Next is the pixel data, which is pointed to by |mDataOffset|.
37 // WinBMPv3. This is the most widely used version.
38 // - It changed the info header to 40 bytes by taking the WinBMPv2 info
39 // header, enlargening its width and height fields, and adding more fields
40 // including: a compression type (|mCompression|) and number of colors
41 // (|mNumColors|).
42 // - The semi-optional color table is now 4 bytes per value (BGR0), and its
43 // length is |mNumColors|, or 2^|mBpp| if |mNumColors| is zero.
44 // - |mCompression| can be RGB (i.e. no compression), RLE4 (if |mBpp|==4) or
45 // RLE8 (if |mBpp|==8) values.
47 // WinBMPv3-NT. A variant of WinBMPv3.
48 // - It did not change the info header layout from WinBMPv3.
49 // - |mBpp| can now be 16 or 32, in which case |mCompression| can be RGB or the
50 // new BITFIELDS value; in the latter case an additional 12 bytes of color
51 // bitfields follow the info header.
53 // WinBMPv4.
54 // - It extended the info header to 108 bytes, including the 12 bytes of color
55 // mask data from WinBMPv3-NT, plus alpha mask data, and also color-space and
56 // gamma correction fields.
58 // WinBMPv5.
59 // - It extended the info header to 124 bytes, adding color profile data.
60 // - It also added an optional color profile table after the pixel data (and
61 // another optional gap).
63 // WinBMPv3-ICO. This is a variant of WinBMPv3.
64 // - It's the BMP format used for BMP images within ICO files.
65 // - The only difference with WinBMPv3 is that if an image is 32bpp and has no
66 // compression, then instead of treating the pixel data as 0RGB it is treated
67 // as ARGB, but only if one or more of the A values are non-zero.
69 // Clipboard variants.
70 // - It's the BMP format used for BMP images captured from the clipboard.
71 // - It is missing the file header, containing the BM signature and the data
72 // offset. Instead the data begins after the header.
73 // - If it uses BITFIELDS compression, then there is always an additional 12
74 // bytes of data after the header that must be read. In WinBMPv4+, the masks
75 // are supposed to be included in the header size, which are the values we use
76 // for decoding purposes, but there is additional three masks following the
77 // header which must be skipped to get to the pixel data.
79 // OS/2 VERSIONS OF THE BMP FORMAT
80 // -------------------------------
81 // OS2-BMPv1.
82 // - Almost identical to WinBMPv2; the differences are basically ignorable.
84 // OS2-BMPv2.
85 // - Similar to WinBMPv3.
86 // - The info header is 64 bytes but can be reduced to as little as 16; any
87 // omitted fields are treated as zero. The first 40 bytes of these fields are
88 // nearly identical to the WinBMPv3 info header; the remaining 24 bytes are
89 // different.
90 // - Also adds compression types "Huffman 1D" and "RLE24", which we don't
91 // support.
92 // - We treat OS2-BMPv2 files as if they are WinBMPv3 (i.e. ignore the extra 24
93 // bytes in the info header), which in practice is good enough.
95 #include "ImageLogging.h"
96 #include "nsBMPDecoder.h"
98 #include <stdlib.h>
100 #include "mozilla/Attributes.h"
101 #include "mozilla/EndianUtils.h"
102 #include "mozilla/Likely.h"
103 #include "mozilla/UniquePtrExtensions.h"
105 #include "RasterImage.h"
106 #include "SurfacePipeFactory.h"
107 #include "gfxPlatform.h"
108 #include <algorithm>
110 using namespace mozilla::gfx;
112 namespace mozilla {
113 namespace image {
114 namespace bmp {
116 struct Compression {
117 enum { RGB = 0, RLE8 = 1, RLE4 = 2, BITFIELDS = 3 };
120 // RLE escape codes and constants.
121 struct RLE {
122 enum {
123 ESCAPE = 0,
124 ESCAPE_EOL = 0,
125 ESCAPE_EOF = 1,
126 ESCAPE_DELTA = 2,
128 SEGMENT_LENGTH = 2,
129 DELTA_LENGTH = 2
133 } // namespace bmp
135 using namespace bmp;
137 static double FixedPoint2Dot30_To_Double(uint32_t aFixed) {
138 constexpr double factor = 1.0 / 1073741824.0; // 2^-30
139 return double(aFixed) * factor;
142 static float FixedPoint16Dot16_To_Float(uint32_t aFixed) {
143 constexpr double factor = 1.0 / 65536.0; // 2^-16
144 return double(aFixed) * factor;
147 static float CalRbgEndpointToQcms(const CalRgbEndpoint& aIn,
148 qcms_CIE_xyY& aOut) {
149 aOut.x = FixedPoint2Dot30_To_Double(aIn.mX);
150 aOut.y = FixedPoint2Dot30_To_Double(aIn.mY);
151 aOut.Y = FixedPoint2Dot30_To_Double(aIn.mZ);
152 return FixedPoint16Dot16_To_Float(aIn.mGamma);
155 static void ReadCalRgbEndpoint(const char* aData, uint32_t aEndpointOffset,
156 uint32_t aGammaOffset, CalRgbEndpoint& aOut) {
157 aOut.mX = LittleEndian::readUint32(aData + aEndpointOffset);
158 aOut.mY = LittleEndian::readUint32(aData + aEndpointOffset + 4);
159 aOut.mZ = LittleEndian::readUint32(aData + aEndpointOffset + 8);
160 aOut.mGamma = LittleEndian::readUint32(aData + aGammaOffset);
163 /// Sets the pixel data in aDecoded to the given values.
164 /// @param aDecoded pointer to pixel to be set, will be incremented to point to
165 /// the next pixel.
166 static void SetPixel(uint32_t*& aDecoded, uint8_t aRed, uint8_t aGreen,
167 uint8_t aBlue, uint8_t aAlpha = 0xFF) {
168 *aDecoded++ = gfxPackedPixelNoPreMultiply(aAlpha, aRed, aGreen, aBlue);
171 static void SetPixel(uint32_t*& aDecoded, uint8_t idx,
172 const UniquePtr<ColorTableEntry[]>& aColors) {
173 SetPixel(aDecoded, aColors[idx].mRed, aColors[idx].mGreen,
174 aColors[idx].mBlue);
177 /// Sets two (or one if aCount = 1) pixels
178 /// @param aDecoded where the data is stored. Will be moved 4 resp 8 bytes
179 /// depending on whether one or two pixels are written.
180 /// @param aData The values for the two pixels
181 /// @param aCount Current count. Is decremented by one or two.
182 static void Set4BitPixel(uint32_t*& aDecoded, uint8_t aData, uint32_t& aCount,
183 const UniquePtr<ColorTableEntry[]>& aColors) {
184 uint8_t idx = aData >> 4;
185 SetPixel(aDecoded, idx, aColors);
186 if (--aCount > 0) {
187 idx = aData & 0xF;
188 SetPixel(aDecoded, idx, aColors);
189 --aCount;
193 static mozilla::LazyLogModule sBMPLog("BMPDecoder");
195 // The length of the mBIHSize field in the info header.
196 static const uint32_t BIHSIZE_FIELD_LENGTH = 4;
198 nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, State aState, size_t aLength,
199 bool aForClipboard)
200 : Decoder(aImage),
201 mLexer(Transition::To(aState, aLength), Transition::TerminateSuccess()),
202 mIsWithinICO(false),
203 mIsForClipboard(aForClipboard),
204 mMayHaveTransparency(false),
205 mDoesHaveTransparency(false),
206 mNumColors(0),
207 mColors(nullptr),
208 mBytesPerColor(0),
209 mPreGapLength(0),
210 mPixelRowSize(0),
211 mCurrentRow(0),
212 mCurrentPos(0),
213 mAbsoluteModeNumPixels(0) {}
215 // Constructor for normal BMP files or from the clipboard.
216 nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, bool aForClipboard)
217 : nsBMPDecoder(aImage,
218 aForClipboard ? State::INFO_HEADER_SIZE : State::FILE_HEADER,
219 aForClipboard ? BIHSIZE_FIELD_LENGTH : FILE_HEADER_LENGTH,
220 aForClipboard) {}
222 // Constructor used for WinBMPv3-ICO files, which lack a file header.
223 nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, uint32_t aDataOffset)
224 : nsBMPDecoder(aImage, State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH,
225 /* aForClipboard */ false) {
226 SetIsWithinICO();
228 // Even though the file header isn't present in this case, the dataOffset
229 // field is set as if it is, and so we must increment mPreGapLength
230 // accordingly.
231 mPreGapLength += FILE_HEADER_LENGTH;
233 // This is the one piece of data we normally get from a BMP file header, so
234 // it must be provided via an argument.
235 mH.mDataOffset = aDataOffset;
238 nsBMPDecoder::~nsBMPDecoder() {}
240 // Obtains the size of the compressed image resource.
241 int32_t nsBMPDecoder::GetCompressedImageSize() const {
242 // In the RGB case mImageSize might not be set, so compute it manually.
243 MOZ_ASSERT(mPixelRowSize != 0);
244 return mH.mCompression == Compression::RGB ? mPixelRowSize * AbsoluteHeight()
245 : mH.mImageSize;
248 nsresult nsBMPDecoder::BeforeFinishInternal() {
249 if (!IsMetadataDecode() && !mImageData) {
250 return NS_ERROR_FAILURE; // No image; something went wrong.
253 return NS_OK;
256 nsresult nsBMPDecoder::FinishInternal() {
257 // We shouldn't be called in error cases.
258 MOZ_ASSERT(!HasError(), "Can't call FinishInternal on error!");
260 // We should never make multiple frames.
261 MOZ_ASSERT(GetFrameCount() <= 1, "Multiple BMP frames?");
263 // Send notifications if appropriate.
264 if (!IsMetadataDecode() && HasSize()) {
265 // We should have image data.
266 MOZ_ASSERT(mImageData);
268 // If it was truncated, fill in the missing pixels as black.
269 while (mCurrentRow > 0) {
270 uint32_t* dst = RowBuffer();
271 while (mCurrentPos < mH.mWidth) {
272 SetPixel(dst, 0, 0, 0);
273 mCurrentPos++;
275 mCurrentPos = 0;
276 FinishRow();
279 MOZ_ASSERT_IF(mDoesHaveTransparency, mMayHaveTransparency);
281 // We have transparency if we either detected some in the image itself
282 // (i.e., |mDoesHaveTransparency| is true) or we're in an ICO, which could
283 // mean we have an AND mask that provides transparency (i.e., |mIsWithinICO|
284 // is true).
285 // XXX(seth): We can tell when we create the decoder if the AND mask is
286 // present, so we could be more precise about this.
287 const Opacity opacity = mDoesHaveTransparency || mIsWithinICO
288 ? Opacity::SOME_TRANSPARENCY
289 : Opacity::FULLY_OPAQUE;
291 PostFrameStop(opacity);
292 PostDecodeDone();
295 return NS_OK;
298 // ----------------------------------------
299 // Actual Data Processing
300 // ----------------------------------------
302 void BitFields::Value::Set(uint32_t aMask) {
303 mMask = aMask;
305 // Handle this exceptional case first. The chosen values don't matter
306 // (because a mask of zero will always give a value of zero) except that
307 // mBitWidth:
308 // - shouldn't be zero, because that would cause an infinite loop in Get();
309 // - shouldn't be 5 or 8, because that could cause a false positive match in
310 // IsR5G5B5() or IsR8G8B8().
311 if (mMask == 0x0) {
312 mRightShift = 0;
313 mBitWidth = 1;
314 return;
317 // Find the rightmost 1.
318 uint8_t i;
319 for (i = 0; i < 32; i++) {
320 if (mMask & (1 << i)) {
321 break;
324 mRightShift = i;
326 // Now find the leftmost 1 in the same run of 1s. (If there are multiple runs
327 // of 1s -- which isn't valid -- we'll behave as if only the lowest run was
328 // present, which seems reasonable.)
329 for (i = i + 1; i < 32; i++) {
330 if (!(mMask & (1 << i))) {
331 break;
334 mBitWidth = i - mRightShift;
337 MOZ_ALWAYS_INLINE uint8_t BitFields::Value::Get(uint32_t aValue) const {
338 // Extract the unscaled value.
339 uint32_t v = (aValue & mMask) >> mRightShift;
341 // Idea: to upscale v precisely we need to duplicate its bits, possibly
342 // repeatedly, possibly partially in the last case, from bit 7 down to bit 0
343 // in v2. For example:
345 // - mBitWidth=1: v2 = v<<7 | v<<6 | ... | v<<1 | v>>0 k -> kkkkkkkk
346 // - mBitWidth=2: v2 = v<<6 | v<<4 | v<<2 | v>>0 jk -> jkjkjkjk
347 // - mBitWidth=3: v2 = v<<5 | v<<2 | v>>1 ijk -> ijkijkij
348 // - mBitWidth=4: v2 = v<<4 | v>>0 hijk -> hijkhijk
349 // - mBitWidth=5: v2 = v<<3 | v>>2 ghijk -> ghijkghi
350 // - mBitWidth=6: v2 = v<<2 | v>>4 fghijk -> fghijkfg
351 // - mBitWidth=7: v2 = v<<1 | v>>6 efghijk -> efghijke
352 // - mBitWidth=8: v2 = v>>0 defghijk -> defghijk
353 // - mBitWidth=9: v2 = v>>1 cdefghijk -> cdefghij
354 // - mBitWidth=10: v2 = v>>2 bcdefghijk -> bcdefghi
355 // - mBitWidth=11: v2 = v>>3 abcdefghijk -> abcdefgh
356 // - etc.
358 uint8_t v2 = 0;
359 int32_t i; // must be a signed integer
360 for (i = 8 - mBitWidth; i > 0; i -= mBitWidth) {
361 v2 |= v << uint32_t(i);
363 v2 |= v >> uint32_t(-i);
364 return v2;
367 MOZ_ALWAYS_INLINE uint8_t BitFields::Value::GetAlpha(uint32_t aValue,
368 bool& aHasAlphaOut) const {
369 if (mMask == 0x0) {
370 return 0xff;
372 aHasAlphaOut = true;
373 return Get(aValue);
376 MOZ_ALWAYS_INLINE uint8_t BitFields::Value::Get5(uint32_t aValue) const {
377 MOZ_ASSERT(mBitWidth == 5);
378 uint32_t v = (aValue & mMask) >> mRightShift;
379 return (v << 3u) | (v >> 2u);
382 MOZ_ALWAYS_INLINE uint8_t BitFields::Value::Get8(uint32_t aValue) const {
383 MOZ_ASSERT(mBitWidth == 8);
384 uint32_t v = (aValue & mMask) >> mRightShift;
385 return v;
388 void BitFields::SetR5G5B5() {
389 mRed.Set(0x7c00);
390 mGreen.Set(0x03e0);
391 mBlue.Set(0x001f);
394 void BitFields::SetR8G8B8() {
395 mRed.Set(0xff0000);
396 mGreen.Set(0xff00);
397 mBlue.Set(0x00ff);
400 bool BitFields::IsR5G5B5() const {
401 return mRed.mBitWidth == 5 && mGreen.mBitWidth == 5 && mBlue.mBitWidth == 5 &&
402 mAlpha.mMask == 0x0;
405 bool BitFields::IsR8G8B8() const {
406 return mRed.mBitWidth == 8 && mGreen.mBitWidth == 8 && mBlue.mBitWidth == 8 &&
407 mAlpha.mMask == 0x0;
410 uint32_t* nsBMPDecoder::RowBuffer() { return mRowBuffer.get() + mCurrentPos; }
412 void nsBMPDecoder::ClearRowBufferRemainder() {
413 int32_t len = mH.mWidth - mCurrentPos;
414 memset(RowBuffer(), mMayHaveTransparency ? 0 : 0xFF, len * sizeof(uint32_t));
417 void nsBMPDecoder::FinishRow() {
418 mPipe.WriteBuffer(mRowBuffer.get());
419 Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect();
420 if (invalidRect) {
421 PostInvalidation(invalidRect->mInputSpaceRect,
422 Some(invalidRect->mOutputSpaceRect));
424 mCurrentRow--;
427 LexerResult nsBMPDecoder::DoDecode(SourceBufferIterator& aIterator,
428 IResumable* aOnResume) {
429 MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
431 return mLexer.Lex(
432 aIterator, aOnResume,
433 [=](State aState, const char* aData, size_t aLength) {
434 switch (aState) {
435 case State::FILE_HEADER:
436 return ReadFileHeader(aData, aLength);
437 case State::INFO_HEADER_SIZE:
438 return ReadInfoHeaderSize(aData, aLength);
439 case State::INFO_HEADER_REST:
440 return ReadInfoHeaderRest(aData, aLength);
441 case State::BITFIELDS:
442 return ReadBitfields(aData, aLength);
443 case State::SKIP_TO_COLOR_PROFILE:
444 return Transition::ContinueUnbuffered(State::SKIP_TO_COLOR_PROFILE);
445 case State::FOUND_COLOR_PROFILE:
446 return Transition::To(State::COLOR_PROFILE,
447 mH.mColorSpace.mProfile.mLength);
448 case State::COLOR_PROFILE:
449 return ReadColorProfile(aData, aLength);
450 case State::ALLOCATE_SURFACE:
451 return AllocateSurface();
452 case State::COLOR_TABLE:
453 return ReadColorTable(aData, aLength);
454 case State::GAP:
455 return SkipGap();
456 case State::AFTER_GAP:
457 return AfterGap();
458 case State::PIXEL_ROW:
459 return ReadPixelRow(aData);
460 case State::RLE_SEGMENT:
461 return ReadRLESegment(aData);
462 case State::RLE_DELTA:
463 return ReadRLEDelta(aData);
464 case State::RLE_ABSOLUTE:
465 return ReadRLEAbsolute(aData, aLength);
466 default:
467 MOZ_CRASH("Unknown State");
472 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadFileHeader(
473 const char* aData, size_t aLength) {
474 mPreGapLength += aLength;
476 bool signatureOk = aData[0] == 'B' && aData[1] == 'M';
477 if (!signatureOk) {
478 return Transition::TerminateFailure();
481 // We ignore the filesize (aData + 2) and reserved (aData + 6) fields.
483 mH.mDataOffset = LittleEndian::readUint32(aData + 10);
485 return Transition::To(State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH);
488 // We read the info header in two steps: (a) read the mBIHSize field to
489 // determine how long the header is; (b) read the rest of the header.
490 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadInfoHeaderSize(
491 const char* aData, size_t aLength) {
492 mH.mBIHSize = LittleEndian::readUint32(aData);
494 // Data offset can be wrong so fix it using the BIH size.
495 if (!mIsForClipboard && mH.mDataOffset < mPreGapLength + mH.mBIHSize) {
496 mH.mDataOffset = mPreGapLength + mH.mBIHSize;
499 mPreGapLength += aLength;
501 bool bihSizeOk = mH.mBIHSize == InfoHeaderLength::WIN_V2 ||
502 mH.mBIHSize == InfoHeaderLength::WIN_V3 ||
503 mH.mBIHSize == InfoHeaderLength::WIN_V4 ||
504 mH.mBIHSize == InfoHeaderLength::WIN_V5 ||
505 (mH.mBIHSize >= InfoHeaderLength::OS2_V2_MIN &&
506 mH.mBIHSize <= InfoHeaderLength::OS2_V2_MAX);
507 if (!bihSizeOk) {
508 return Transition::TerminateFailure();
510 // ICO BMPs must have a WinBMPv3 header. nsICODecoder should have already
511 // terminated decoding if this isn't the case.
512 MOZ_ASSERT_IF(mIsWithinICO, mH.mBIHSize == InfoHeaderLength::WIN_V3);
514 return Transition::To(State::INFO_HEADER_REST,
515 mH.mBIHSize - BIHSIZE_FIELD_LENGTH);
518 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadInfoHeaderRest(
519 const char* aData, size_t aLength) {
520 mPreGapLength += aLength;
522 // |mWidth| and |mHeight| may be signed (Windows) or unsigned (OS/2). We just
523 // read as unsigned because in practice that's good enough.
524 if (mH.mBIHSize == InfoHeaderLength::WIN_V2) {
525 mH.mWidth = LittleEndian::readUint16(aData + 0);
526 mH.mHeight = LittleEndian::readUint16(aData + 2);
527 // We ignore the planes (aData + 4) field; it should always be 1.
528 mH.mBpp = LittleEndian::readUint16(aData + 6);
529 } else {
530 mH.mWidth = LittleEndian::readUint32(aData + 0);
531 mH.mHeight = LittleEndian::readUint32(aData + 4);
532 // We ignore the planes (aData + 4) field; it should always be 1.
533 mH.mBpp = LittleEndian::readUint16(aData + 10);
535 // For OS2-BMPv2 the info header may be as little as 16 bytes, so be
536 // careful for these fields.
537 mH.mCompression = aLength >= 16 ? LittleEndian::readUint32(aData + 12) : 0;
538 mH.mImageSize = aLength >= 20 ? LittleEndian::readUint32(aData + 16) : 0;
539 // We ignore the xppm (aData + 20) and yppm (aData + 24) fields.
540 mH.mNumColors = aLength >= 32 ? LittleEndian::readUint32(aData + 28) : 0;
541 // We ignore the important_colors (aData + 36) field.
543 // Read color management properties we may need later.
544 mH.mCsType =
545 aLength >= 56
546 ? static_cast<InfoColorSpace>(LittleEndian::readUint32(aData + 52))
547 : InfoColorSpace::SRGB;
548 mH.mCsIntent = aLength >= 108 ? static_cast<InfoColorIntent>(
549 LittleEndian::readUint32(aData + 104))
550 : InfoColorIntent::IMAGES;
552 switch (mH.mCsType) {
553 case InfoColorSpace::CALIBRATED_RGB:
554 if (aLength >= 104) {
555 ReadCalRgbEndpoint(aData, 56, 92, mH.mColorSpace.mCalibrated.mRed);
556 ReadCalRgbEndpoint(aData, 68, 96, mH.mColorSpace.mCalibrated.mGreen);
557 ReadCalRgbEndpoint(aData, 80, 100, mH.mColorSpace.mCalibrated.mBlue);
558 } else {
559 mH.mCsType = InfoColorSpace::SRGB;
561 break;
562 case InfoColorSpace::EMBEDDED:
563 if (aLength >= 116) {
564 mH.mColorSpace.mProfile.mOffset =
565 LittleEndian::readUint32(aData + 108);
566 mH.mColorSpace.mProfile.mLength =
567 LittleEndian::readUint32(aData + 112);
568 } else {
569 mH.mCsType = InfoColorSpace::SRGB;
571 break;
572 case InfoColorSpace::LINKED:
573 case InfoColorSpace::SRGB:
574 case InfoColorSpace::WIN:
575 default:
576 // Nothing to be done at this time.
577 break;
580 // For WinBMPv4, WinBMPv5 and (possibly) OS2-BMPv2 there are additional
581 // fields in the info header which we ignore, with the possible exception
582 // of the color bitfields (see below).
585 // The height for BMPs embedded inside an ICO includes spaces for the AND
586 // mask even if it is not present, thus we need to adjust for that here.
587 if (mIsWithinICO) {
588 // XXX(seth): Should we really be writing the absolute value from
589 // the BIH below? Seems like this could be problematic for inverted BMPs.
590 mH.mHeight = abs(mH.mHeight) / 2;
593 // Run with MOZ_LOG=BMPDecoder:5 set to see this output.
594 MOZ_LOG(sBMPLog, LogLevel::Debug,
595 ("BMP: bihsize=%u, %d x %d, bpp=%u, compression=%u, colors=%u, "
596 "data-offset=%u\n",
597 mH.mBIHSize, mH.mWidth, mH.mHeight, uint32_t(mH.mBpp),
598 mH.mCompression, mH.mNumColors, mH.mDataOffset));
600 // BMPs with negative width are invalid. Also, reject extremely wide images
601 // to keep the math sane. And reject INT_MIN as a height because you can't
602 // get its absolute value (because -INT_MIN is one more than INT_MAX).
603 const int32_t k64KWidth = 0x0000FFFF;
604 bool sizeOk =
605 0 <= mH.mWidth && mH.mWidth <= k64KWidth && mH.mHeight != INT_MIN;
606 if (!sizeOk) {
607 return Transition::TerminateFailure();
610 // Check mBpp and mCompression.
611 bool bppCompressionOk =
612 (mH.mCompression == Compression::RGB &&
613 (mH.mBpp == 1 || mH.mBpp == 4 || mH.mBpp == 8 || mH.mBpp == 16 ||
614 mH.mBpp == 24 || mH.mBpp == 32)) ||
615 (mH.mCompression == Compression::RLE8 && mH.mBpp == 8) ||
616 (mH.mCompression == Compression::RLE4 && mH.mBpp == 4) ||
617 (mH.mCompression == Compression::BITFIELDS &&
618 // For BITFIELDS compression we require an exact match for one of the
619 // WinBMP BIH sizes; this clearly isn't an OS2 BMP.
620 (mH.mBIHSize == InfoHeaderLength::WIN_V3 ||
621 mH.mBIHSize == InfoHeaderLength::WIN_V4 ||
622 mH.mBIHSize == InfoHeaderLength::WIN_V5) &&
623 (mH.mBpp == 16 || mH.mBpp == 32));
624 if (!bppCompressionOk) {
625 return Transition::TerminateFailure();
628 // Initialize our current row to the top of the image.
629 mCurrentRow = AbsoluteHeight();
631 // Round it up to the nearest byte count, then pad to 4-byte boundary.
632 // Compute this even for a metadate decode because GetCompressedImageSize()
633 // relies on it.
634 mPixelRowSize = (mH.mBpp * mH.mWidth + 7) / 8;
635 uint32_t surplus = mPixelRowSize % 4;
636 if (surplus != 0) {
637 mPixelRowSize += 4 - surplus;
640 size_t bitFieldsLengthStillToRead = 0;
641 if (mH.mCompression == Compression::BITFIELDS) {
642 // Need to read bitfields.
643 if (mH.mBIHSize >= InfoHeaderLength::WIN_V4) {
644 // Bitfields are present in the info header, so we can read them
645 // immediately.
646 mBitFields.ReadFromHeader(aData + 36, /* aReadAlpha = */ true);
648 // If this came from the clipboard, then we know that even if the header
649 // explicitly includes the bitfield masks, we need to add an additional
650 // offset for the start of the RGB data.
651 if (mIsForClipboard) {
652 mH.mDataOffset += BitFields::LENGTH;
654 } else {
655 // Bitfields are present after the info header, so we will read them in
656 // ReadBitfields().
657 bitFieldsLengthStillToRead = BitFields::LENGTH;
659 } else if (mH.mBpp == 16) {
660 // No bitfields specified; use the default 5-5-5 values.
661 mBitFields.SetR5G5B5();
662 } else if (mH.mBpp == 32) {
663 // No bitfields specified; use the default 8-8-8 values.
664 mBitFields.SetR8G8B8();
667 return Transition::To(State::BITFIELDS, bitFieldsLengthStillToRead);
670 void BitFields::ReadFromHeader(const char* aData, bool aReadAlpha) {
671 mRed.Set(LittleEndian::readUint32(aData + 0));
672 mGreen.Set(LittleEndian::readUint32(aData + 4));
673 mBlue.Set(LittleEndian::readUint32(aData + 8));
674 if (aReadAlpha) {
675 mAlpha.Set(LittleEndian::readUint32(aData + 12));
679 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadBitfields(
680 const char* aData, size_t aLength) {
681 mPreGapLength += aLength;
683 // If aLength is zero there are no bitfields to read, or we already read them
684 // in ReadInfoHeader().
685 if (aLength != 0) {
686 mBitFields.ReadFromHeader(aData, /* aReadAlpha = */ false);
689 // Note that RLE-encoded BMPs might be transparent because the 'delta' mode
690 // can skip pixels and cause implicit transparency.
691 mMayHaveTransparency = mIsWithinICO || mH.mCompression == Compression::RLE8 ||
692 mH.mCompression == Compression::RLE4 ||
693 (mH.mCompression == Compression::BITFIELDS &&
694 mBitFields.mAlpha.IsPresent());
695 if (mMayHaveTransparency) {
696 PostHasTransparency();
699 // Post our size to the superclass.
700 PostSize(mH.mWidth, AbsoluteHeight());
701 if (WantsFrameCount()) {
702 PostFrameCount(/* aFrameCount */ 1);
704 if (HasError()) {
705 return Transition::TerminateFailure();
708 // We've now read all the headers. If we're doing a metadata decode, we're
709 // done.
710 if (IsMetadataDecode()) {
711 return Transition::TerminateSuccess();
714 // Set up the color table, if present; it'll be filled in by ReadColorTable().
715 if (mH.mBpp <= 8) {
716 mNumColors = 1 << mH.mBpp;
717 if (0 < mH.mNumColors && mH.mNumColors < mNumColors) {
718 mNumColors = mH.mNumColors;
721 // Always allocate and zero 256 entries, even though mNumColors might be
722 // smaller, because the file might erroneously index past mNumColors.
723 mColors = MakeUniqueFallible<ColorTableEntry[]>(256);
724 if (NS_WARN_IF(!mColors)) {
725 return Transition::TerminateFailure();
727 memset(mColors.get(), 0, 256 * sizeof(ColorTableEntry));
729 // OS/2 Bitmaps have no padding byte.
730 mBytesPerColor = (mH.mBIHSize == InfoHeaderLength::WIN_V2) ? 3 : 4;
733 if (mCMSMode != CMSMode::Off) {
734 switch (mH.mCsType) {
735 case InfoColorSpace::EMBEDDED:
736 return SeekColorProfile(aLength);
737 case InfoColorSpace::CALIBRATED_RGB:
738 PrepareCalibratedColorProfile();
739 break;
740 case InfoColorSpace::SRGB:
741 case InfoColorSpace::WIN:
742 MOZ_LOG(sBMPLog, LogLevel::Debug, ("using sRGB color profile\n"));
743 if (mColors) {
744 // We will transform the color table instead of the output pixels.
745 mTransform = GetCMSsRGBTransform(SurfaceFormat::R8G8B8);
746 } else {
747 mTransform = GetCMSsRGBTransform(SurfaceFormat::OS_RGBA);
749 break;
750 case InfoColorSpace::LINKED:
751 default:
752 // Not supported, no color management.
753 MOZ_LOG(sBMPLog, LogLevel::Debug, ("color space type not provided\n"));
754 break;
758 return Transition::To(State::ALLOCATE_SURFACE, 0);
761 void nsBMPDecoder::PrepareCalibratedColorProfile() {
762 // BMP does not define a white point. Use the same as sRGB. This matches what
763 // Chrome does as well.
764 qcms_CIE_xyY white_point = qcms_white_point_sRGB();
766 qcms_CIE_xyYTRIPLE primaries;
767 float redGamma =
768 CalRbgEndpointToQcms(mH.mColorSpace.mCalibrated.mRed, primaries.red);
769 float greenGamma =
770 CalRbgEndpointToQcms(mH.mColorSpace.mCalibrated.mGreen, primaries.green);
771 float blueGamma =
772 CalRbgEndpointToQcms(mH.mColorSpace.mCalibrated.mBlue, primaries.blue);
774 // Explicitly verify the profile because sometimes the values from the BMP
775 // header are just garbage.
776 mInProfile = qcms_profile_create_rgb_with_gamma_set(
777 white_point, primaries, redGamma, greenGamma, blueGamma);
778 if (mInProfile && qcms_profile_is_bogus(mInProfile)) {
779 // Bad profile, just use sRGB instead. Release the profile here, so that
780 // our destructor doesn't assume we are the owner for the transform.
781 qcms_profile_release(mInProfile);
782 mInProfile = nullptr;
785 if (mInProfile) {
786 MOZ_LOG(sBMPLog, LogLevel::Debug, ("using calibrated RGB color profile\n"));
787 PrepareColorProfileTransform();
788 } else {
789 MOZ_LOG(sBMPLog, LogLevel::Debug,
790 ("failed to create calibrated RGB color profile, using sRGB\n"));
791 if (mColors) {
792 // We will transform the color table instead of the output pixels.
793 mTransform = GetCMSsRGBTransform(SurfaceFormat::R8G8B8);
794 } else {
795 mTransform = GetCMSsRGBTransform(SurfaceFormat::OS_RGBA);
800 void nsBMPDecoder::PrepareColorProfileTransform() {
801 if (!mInProfile || !GetCMSOutputProfile()) {
802 return;
805 qcms_data_type inType;
806 qcms_data_type outType;
807 if (mColors) {
808 // We will transform the color table instead of the output pixels.
809 inType = QCMS_DATA_RGB_8;
810 outType = QCMS_DATA_RGB_8;
811 } else {
812 inType = gfxPlatform::GetCMSOSRGBAType();
813 outType = inType;
816 qcms_intent intent;
817 switch (mH.mCsIntent) {
818 case InfoColorIntent::BUSINESS:
819 intent = QCMS_INTENT_SATURATION;
820 break;
821 case InfoColorIntent::GRAPHICS:
822 intent = QCMS_INTENT_RELATIVE_COLORIMETRIC;
823 break;
824 case InfoColorIntent::ABS_COLORIMETRIC:
825 intent = QCMS_INTENT_ABSOLUTE_COLORIMETRIC;
826 break;
827 case InfoColorIntent::IMAGES:
828 default:
829 intent = QCMS_INTENT_PERCEPTUAL;
830 break;
833 mTransform = qcms_transform_create(mInProfile, inType, GetCMSOutputProfile(),
834 outType, intent);
835 if (!mTransform) {
836 MOZ_LOG(sBMPLog, LogLevel::Debug,
837 ("failed to create color profile transform\n"));
841 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::SeekColorProfile(
842 size_t aLength) {
843 // The offset needs to be at least after the color table.
844 uint32_t offset = mH.mColorSpace.mProfile.mOffset;
845 if (offset <= mH.mBIHSize + aLength + mNumColors * mBytesPerColor ||
846 mH.mColorSpace.mProfile.mLength == 0) {
847 return Transition::To(State::ALLOCATE_SURFACE, 0);
850 // We have already read the header and bitfields.
851 offset -= mH.mBIHSize + aLength;
853 // We need to skip ahead to search for the embedded color profile. We want
854 // to return to this point once we read it.
855 mReturnIterator = mLexer.Clone(*mIterator, SIZE_MAX);
856 if (!mReturnIterator) {
857 return Transition::TerminateFailure();
860 return Transition::ToUnbuffered(State::FOUND_COLOR_PROFILE,
861 State::SKIP_TO_COLOR_PROFILE, offset);
864 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadColorProfile(
865 const char* aData, size_t aLength) {
866 mInProfile = qcms_profile_from_memory(aData, aLength);
867 if (mInProfile) {
868 MOZ_LOG(sBMPLog, LogLevel::Debug, ("using embedded color profile\n"));
869 PrepareColorProfileTransform();
872 // Jump back to where we left off.
873 mIterator = std::move(mReturnIterator);
874 return Transition::To(State::ALLOCATE_SURFACE, 0);
877 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::AllocateSurface() {
878 SurfaceFormat format;
879 SurfacePipeFlags pipeFlags = SurfacePipeFlags();
881 if (mMayHaveTransparency) {
882 format = SurfaceFormat::OS_RGBA;
883 if (!(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA)) {
884 pipeFlags |= SurfacePipeFlags::PREMULTIPLY_ALPHA;
886 } else {
887 format = SurfaceFormat::OS_RGBX;
890 if (mH.mHeight >= 0) {
891 // BMPs store their rows in reverse order, so we may need to flip.
892 pipeFlags |= SurfacePipeFlags::FLIP_VERTICALLY;
895 mRowBuffer.reset(new (fallible) uint32_t[mH.mWidth]);
896 if (!mRowBuffer) {
897 return Transition::TerminateFailure();
900 // Only give the color transform to the SurfacePipe if we are not transforming
901 // the color table in advance.
902 qcms_transform* transform = mColors ? nullptr : mTransform;
904 Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
905 this, Size(), OutputSize(), FullFrame(), format, format, Nothing(),
906 transform, pipeFlags);
907 if (!pipe) {
908 return Transition::TerminateFailure();
911 mPipe = std::move(*pipe);
912 ClearRowBufferRemainder();
913 return Transition::To(State::COLOR_TABLE, mNumColors * mBytesPerColor);
916 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadColorTable(
917 const char* aData, size_t aLength) {
918 MOZ_ASSERT_IF(aLength != 0, mNumColors > 0 && mColors);
920 mPreGapLength += aLength;
922 for (uint32_t i = 0; i < mNumColors; i++) {
923 // The format is BGR or BGR0.
924 mColors[i].mBlue = uint8_t(aData[0]);
925 mColors[i].mGreen = uint8_t(aData[1]);
926 mColors[i].mRed = uint8_t(aData[2]);
927 aData += mBytesPerColor;
930 // If we have a color table and a transform, we can avoid transforming each
931 // pixel by doing the table in advance. We color manage every entry in the
932 // table, even if it is smaller in case the BMP is malformed and overruns
933 // its stated color range.
934 if (mColors && mTransform) {
935 qcms_transform_data(mTransform, mColors.get(), mColors.get(), 256);
938 // If we are decoding a BMP from the clipboard, we did not know the data
939 // offset in advance. It is just defined as after the header and color table.
940 if (mIsForClipboard) {
941 mH.mDataOffset += mPreGapLength;
944 // We know how many bytes we've read so far (mPreGapLength) and we know the
945 // offset of the pixel data (mH.mDataOffset), so we can determine the length
946 // of the gap (possibly zero) between the color table and the pixel data.
948 // If the gap is negative the file must be malformed (e.g. mH.mDataOffset
949 // points into the middle of the color palette instead of past the end) and
950 // we give up.
951 if (mPreGapLength > mH.mDataOffset) {
952 return Transition::TerminateFailure();
955 uint32_t gapLength = mH.mDataOffset - mPreGapLength;
957 return Transition::ToUnbuffered(State::AFTER_GAP, State::GAP, gapLength);
960 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::SkipGap() {
961 return Transition::ContinueUnbuffered(State::GAP);
964 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::AfterGap() {
965 // If there are no pixels we can stop.
967 // XXX: normally, if there are no pixels we will have stopped decoding before
968 // now, outside of this decoder. However, if the BMP is within an ICO file,
969 // it's possible that the ICO claimed the image had a non-zero size while the
970 // BMP claims otherwise. This test is to catch that awkward case. If we ever
971 // come up with a more general solution to this ICO-and-BMP-disagree-on-size
972 // problem, this test can be removed.
973 if (mH.mWidth == 0 || mH.mHeight == 0) {
974 return Transition::TerminateSuccess();
977 bool hasRLE = mH.mCompression == Compression::RLE8 ||
978 mH.mCompression == Compression::RLE4;
979 return hasRLE ? Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH)
980 : Transition::To(State::PIXEL_ROW, mPixelRowSize);
983 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadPixelRow(
984 const char* aData) {
985 MOZ_ASSERT(mCurrentRow > 0);
986 MOZ_ASSERT(mCurrentPos == 0);
988 const uint8_t* src = reinterpret_cast<const uint8_t*>(aData);
989 uint32_t* dst = RowBuffer();
990 uint32_t lpos = mH.mWidth;
991 switch (mH.mBpp) {
992 case 1:
993 while (lpos > 0) {
994 int8_t bit;
995 uint8_t idx;
996 for (bit = 7; bit >= 0 && lpos > 0; bit--) {
997 idx = (*src >> bit) & 1;
998 SetPixel(dst, idx, mColors);
999 --lpos;
1001 ++src;
1003 break;
1005 case 4:
1006 while (lpos > 0) {
1007 Set4BitPixel(dst, *src, lpos, mColors);
1008 ++src;
1010 break;
1012 case 8:
1013 while (lpos > 0) {
1014 SetPixel(dst, *src, mColors);
1015 --lpos;
1016 ++src;
1018 break;
1020 case 16:
1021 if (mBitFields.IsR5G5B5()) {
1022 // Specialize this common case.
1023 while (lpos > 0) {
1024 uint16_t val = LittleEndian::readUint16(src);
1025 SetPixel(dst, mBitFields.mRed.Get5(val), mBitFields.mGreen.Get5(val),
1026 mBitFields.mBlue.Get5(val));
1027 --lpos;
1028 src += 2;
1030 } else {
1031 bool anyHasAlpha = false;
1032 while (lpos > 0) {
1033 uint16_t val = LittleEndian::readUint16(src);
1034 SetPixel(dst, mBitFields.mRed.Get(val), mBitFields.mGreen.Get(val),
1035 mBitFields.mBlue.Get(val),
1036 mBitFields.mAlpha.GetAlpha(val, anyHasAlpha));
1037 --lpos;
1038 src += 2;
1040 if (anyHasAlpha) {
1041 MOZ_ASSERT(mMayHaveTransparency);
1042 mDoesHaveTransparency = true;
1045 break;
1047 case 24:
1048 while (lpos > 0) {
1049 SetPixel(dst, src[2], src[1], src[0]);
1050 --lpos;
1051 src += 3;
1053 break;
1055 case 32:
1056 if (mH.mCompression == Compression::RGB && mIsWithinICO &&
1057 mH.mBpp == 32) {
1058 // This is a special case only used for 32bpp WinBMPv3-ICO files, which
1059 // could be in either 0RGB or ARGB format. We start by assuming it's
1060 // an 0RGB image. If we hit a non-zero alpha value, then we know it's
1061 // actually an ARGB image, and change tack accordingly.
1062 // (Note: a fully-transparent ARGB image is indistinguishable from a
1063 // 0RGB image, and we will render such an image as a 0RGB image, i.e.
1064 // opaquely. This is unlikely to be a problem in practice.)
1065 while (lpos > 0) {
1066 if (!mDoesHaveTransparency && src[3] != 0) {
1067 // Up until now this looked like an 0RGB image, but we now know
1068 // it's actually an ARGB image. Which means every pixel we've seen
1069 // so far has been fully transparent. So we go back and redo them.
1071 // Tell the SurfacePipe to go back to the start.
1072 mPipe.ResetToFirstRow();
1074 // Redo the complete rows we've already done.
1075 MOZ_ASSERT(mCurrentPos == 0);
1076 int32_t currentRow = mCurrentRow;
1077 mCurrentRow = AbsoluteHeight();
1078 ClearRowBufferRemainder();
1079 while (mCurrentRow > currentRow) {
1080 FinishRow();
1083 // Reset the row pointer back to where we started.
1084 dst = RowBuffer() + (mH.mWidth - lpos);
1086 MOZ_ASSERT(mMayHaveTransparency);
1087 mDoesHaveTransparency = true;
1090 // If mDoesHaveTransparency is false, treat this as an 0RGB image.
1091 // Otherwise, treat this as an ARGB image.
1092 SetPixel(dst, src[2], src[1], src[0],
1093 mDoesHaveTransparency ? src[3] : 0xff);
1094 src += 4;
1095 --lpos;
1097 } else if (mBitFields.IsR8G8B8()) {
1098 // Specialize this common case.
1099 while (lpos > 0) {
1100 uint32_t val = LittleEndian::readUint32(src);
1101 SetPixel(dst, mBitFields.mRed.Get8(val), mBitFields.mGreen.Get8(val),
1102 mBitFields.mBlue.Get8(val));
1103 --lpos;
1104 src += 4;
1106 } else {
1107 bool anyHasAlpha = false;
1108 while (lpos > 0) {
1109 uint32_t val = LittleEndian::readUint32(src);
1110 SetPixel(dst, mBitFields.mRed.Get(val), mBitFields.mGreen.Get(val),
1111 mBitFields.mBlue.Get(val),
1112 mBitFields.mAlpha.GetAlpha(val, anyHasAlpha));
1113 --lpos;
1114 src += 4;
1116 if (anyHasAlpha) {
1117 MOZ_ASSERT(mMayHaveTransparency);
1118 mDoesHaveTransparency = true;
1121 break;
1123 default:
1124 MOZ_CRASH("Unsupported color depth; earlier check didn't catch it?");
1127 FinishRow();
1128 return mCurrentRow == 0 ? Transition::TerminateSuccess()
1129 : Transition::To(State::PIXEL_ROW, mPixelRowSize);
1132 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadRLESegment(
1133 const char* aData) {
1134 if (mCurrentRow == 0) {
1135 return Transition::TerminateSuccess();
1138 uint8_t byte1 = uint8_t(aData[0]);
1139 uint8_t byte2 = uint8_t(aData[1]);
1141 if (byte1 != RLE::ESCAPE) {
1142 // Encoded mode consists of two bytes: byte1 specifies the number of
1143 // consecutive pixels to be drawn using the color index contained in
1144 // byte2.
1146 // Work around bitmaps that specify too many pixels.
1147 uint32_t pixelsNeeded = std::min<uint32_t>(mH.mWidth - mCurrentPos, byte1);
1148 if (pixelsNeeded) {
1149 uint32_t* dst = RowBuffer();
1150 mCurrentPos += pixelsNeeded;
1151 if (mH.mCompression == Compression::RLE8) {
1152 do {
1153 SetPixel(dst, byte2, mColors);
1154 pixelsNeeded--;
1155 } while (pixelsNeeded);
1156 } else {
1157 do {
1158 Set4BitPixel(dst, byte2, pixelsNeeded, mColors);
1159 } while (pixelsNeeded);
1162 return Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1165 if (byte2 == RLE::ESCAPE_EOL) {
1166 ClearRowBufferRemainder();
1167 mCurrentPos = 0;
1168 FinishRow();
1169 return mCurrentRow == 0
1170 ? Transition::TerminateSuccess()
1171 : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1174 if (byte2 == RLE::ESCAPE_EOF) {
1175 return Transition::TerminateSuccess();
1178 if (byte2 == RLE::ESCAPE_DELTA) {
1179 return Transition::To(State::RLE_DELTA, RLE::DELTA_LENGTH);
1182 // Absolute mode. |byte2| gives the number of pixels. The length depends on
1183 // whether it's 4-bit or 8-bit RLE. Also, the length must be even (and zero
1184 // padding is used to achieve this when necessary).
1185 MOZ_ASSERT(mAbsoluteModeNumPixels == 0);
1186 mAbsoluteModeNumPixels = byte2;
1187 uint32_t length = byte2;
1188 if (mH.mCompression == Compression::RLE4) {
1189 length = (length + 1) / 2; // halve, rounding up
1191 if (length & 1) {
1192 length++;
1194 return Transition::To(State::RLE_ABSOLUTE, length);
1197 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadRLEDelta(
1198 const char* aData) {
1199 // Delta encoding makes it possible to skip pixels making part of the image
1200 // transparent.
1201 MOZ_ASSERT(mMayHaveTransparency);
1202 mDoesHaveTransparency = true;
1204 // Clear the skipped pixels. (This clears to the end of the row,
1205 // which is perfect if there's a Y delta and harmless if not).
1206 ClearRowBufferRemainder();
1208 // Handle the XDelta.
1209 mCurrentPos += uint8_t(aData[0]);
1210 if (mCurrentPos > mH.mWidth) {
1211 mCurrentPos = mH.mWidth;
1214 // Handle the Y Delta.
1215 int32_t yDelta = std::min<int32_t>(uint8_t(aData[1]), mCurrentRow);
1216 if (yDelta > 0) {
1217 // Commit the current row (the first of the skipped rows).
1218 FinishRow();
1220 // Clear and commit the remaining skipped rows. We want to be careful not
1221 // to change mCurrentPos here.
1222 memset(mRowBuffer.get(), 0, mH.mWidth * sizeof(uint32_t));
1223 for (int32_t line = 1; line < yDelta; line++) {
1224 FinishRow();
1228 return mCurrentRow == 0
1229 ? Transition::TerminateSuccess()
1230 : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1233 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadRLEAbsolute(
1234 const char* aData, size_t aLength) {
1235 uint32_t n = mAbsoluteModeNumPixels;
1236 mAbsoluteModeNumPixels = 0;
1238 if (mCurrentPos + n > uint32_t(mH.mWidth)) {
1239 // Some DIB RLE8 encoders count a padding byte as the absolute mode
1240 // pixel number at the end of the row.
1241 if (mH.mCompression == Compression::RLE8 && n > 0 && (n & 1) == 0 &&
1242 mCurrentPos + n - uint32_t(mH.mWidth) == 1 && aLength > 0 &&
1243 aData[aLength - 1] == 0) {
1244 n--;
1245 } else {
1246 // Bad data. Stop decoding; at least part of the image may have been
1247 // decoded.
1248 return Transition::TerminateSuccess();
1252 // In absolute mode, n represents the number of pixels that follow, each of
1253 // which contains the color index of a single pixel.
1254 uint32_t* dst = RowBuffer();
1255 uint32_t iSrc = 0;
1256 uint32_t* oldPos = dst;
1257 if (mH.mCompression == Compression::RLE8) {
1258 while (n > 0) {
1259 SetPixel(dst, aData[iSrc], mColors);
1260 n--;
1261 iSrc++;
1263 } else {
1264 while (n > 0) {
1265 Set4BitPixel(dst, aData[iSrc], n, mColors);
1266 iSrc++;
1269 mCurrentPos += dst - oldPos;
1271 // We should read all the data (unless the last byte is zero padding).
1272 MOZ_ASSERT(iSrc == aLength - 1 || iSrc == aLength);
1274 return Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1277 } // namespace image
1278 } // namespace mozilla