Bug 1885094 - Switch to LaFibre.info for Snap QA download test r=mboldan
[gecko.git] / image / decoders / nsBMPDecoder.cpp
blobda971e054faf7752fc593eebfedea30a58bb7d74
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 (HasError()) {
702 return Transition::TerminateFailure();
705 // We've now read all the headers. If we're doing a metadata decode, we're
706 // done.
707 if (IsMetadataDecode()) {
708 return Transition::TerminateSuccess();
711 // Set up the color table, if present; it'll be filled in by ReadColorTable().
712 if (mH.mBpp <= 8) {
713 mNumColors = 1 << mH.mBpp;
714 if (0 < mH.mNumColors && mH.mNumColors < mNumColors) {
715 mNumColors = mH.mNumColors;
718 // Always allocate and zero 256 entries, even though mNumColors might be
719 // smaller, because the file might erroneously index past mNumColors.
720 mColors = MakeUniqueFallible<ColorTableEntry[]>(256);
721 if (NS_WARN_IF(!mColors)) {
722 return Transition::TerminateFailure();
724 memset(mColors.get(), 0, 256 * sizeof(ColorTableEntry));
726 // OS/2 Bitmaps have no padding byte.
727 mBytesPerColor = (mH.mBIHSize == InfoHeaderLength::WIN_V2) ? 3 : 4;
730 if (mCMSMode != CMSMode::Off) {
731 switch (mH.mCsType) {
732 case InfoColorSpace::EMBEDDED:
733 return SeekColorProfile(aLength);
734 case InfoColorSpace::CALIBRATED_RGB:
735 PrepareCalibratedColorProfile();
736 break;
737 case InfoColorSpace::SRGB:
738 case InfoColorSpace::WIN:
739 MOZ_LOG(sBMPLog, LogLevel::Debug, ("using sRGB color profile\n"));
740 if (mColors) {
741 // We will transform the color table instead of the output pixels.
742 mTransform = GetCMSsRGBTransform(SurfaceFormat::R8G8B8);
743 } else {
744 mTransform = GetCMSsRGBTransform(SurfaceFormat::OS_RGBA);
746 break;
747 case InfoColorSpace::LINKED:
748 default:
749 // Not supported, no color management.
750 MOZ_LOG(sBMPLog, LogLevel::Debug, ("color space type not provided\n"));
751 break;
755 return Transition::To(State::ALLOCATE_SURFACE, 0);
758 void nsBMPDecoder::PrepareCalibratedColorProfile() {
759 // BMP does not define a white point. Use the same as sRGB. This matches what
760 // Chrome does as well.
761 qcms_CIE_xyY white_point = qcms_white_point_sRGB();
763 qcms_CIE_xyYTRIPLE primaries;
764 float redGamma =
765 CalRbgEndpointToQcms(mH.mColorSpace.mCalibrated.mRed, primaries.red);
766 float greenGamma =
767 CalRbgEndpointToQcms(mH.mColorSpace.mCalibrated.mGreen, primaries.green);
768 float blueGamma =
769 CalRbgEndpointToQcms(mH.mColorSpace.mCalibrated.mBlue, primaries.blue);
771 // Explicitly verify the profile because sometimes the values from the BMP
772 // header are just garbage.
773 mInProfile = qcms_profile_create_rgb_with_gamma_set(
774 white_point, primaries, redGamma, greenGamma, blueGamma);
775 if (mInProfile && qcms_profile_is_bogus(mInProfile)) {
776 // Bad profile, just use sRGB instead. Release the profile here, so that
777 // our destructor doesn't assume we are the owner for the transform.
778 qcms_profile_release(mInProfile);
779 mInProfile = nullptr;
782 if (mInProfile) {
783 MOZ_LOG(sBMPLog, LogLevel::Debug, ("using calibrated RGB color profile\n"));
784 PrepareColorProfileTransform();
785 } else {
786 MOZ_LOG(sBMPLog, LogLevel::Debug,
787 ("failed to create calibrated RGB color profile, using sRGB\n"));
788 if (mColors) {
789 // We will transform the color table instead of the output pixels.
790 mTransform = GetCMSsRGBTransform(SurfaceFormat::R8G8B8);
791 } else {
792 mTransform = GetCMSsRGBTransform(SurfaceFormat::OS_RGBA);
797 void nsBMPDecoder::PrepareColorProfileTransform() {
798 if (!mInProfile || !GetCMSOutputProfile()) {
799 return;
802 qcms_data_type inType;
803 qcms_data_type outType;
804 if (mColors) {
805 // We will transform the color table instead of the output pixels.
806 inType = QCMS_DATA_RGB_8;
807 outType = QCMS_DATA_RGB_8;
808 } else {
809 inType = gfxPlatform::GetCMSOSRGBAType();
810 outType = inType;
813 qcms_intent intent;
814 switch (mH.mCsIntent) {
815 case InfoColorIntent::BUSINESS:
816 intent = QCMS_INTENT_SATURATION;
817 break;
818 case InfoColorIntent::GRAPHICS:
819 intent = QCMS_INTENT_RELATIVE_COLORIMETRIC;
820 break;
821 case InfoColorIntent::ABS_COLORIMETRIC:
822 intent = QCMS_INTENT_ABSOLUTE_COLORIMETRIC;
823 break;
824 case InfoColorIntent::IMAGES:
825 default:
826 intent = QCMS_INTENT_PERCEPTUAL;
827 break;
830 mTransform = qcms_transform_create(mInProfile, inType, GetCMSOutputProfile(),
831 outType, intent);
832 if (!mTransform) {
833 MOZ_LOG(sBMPLog, LogLevel::Debug,
834 ("failed to create color profile transform\n"));
838 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::SeekColorProfile(
839 size_t aLength) {
840 // The offset needs to be at least after the color table.
841 uint32_t offset = mH.mColorSpace.mProfile.mOffset;
842 if (offset <= mH.mBIHSize + aLength + mNumColors * mBytesPerColor ||
843 mH.mColorSpace.mProfile.mLength == 0) {
844 return Transition::To(State::ALLOCATE_SURFACE, 0);
847 // We have already read the header and bitfields.
848 offset -= mH.mBIHSize + aLength;
850 // We need to skip ahead to search for the embedded color profile. We want
851 // to return to this point once we read it.
852 mReturnIterator = mLexer.Clone(*mIterator, SIZE_MAX);
853 if (!mReturnIterator) {
854 return Transition::TerminateFailure();
857 return Transition::ToUnbuffered(State::FOUND_COLOR_PROFILE,
858 State::SKIP_TO_COLOR_PROFILE, offset);
861 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadColorProfile(
862 const char* aData, size_t aLength) {
863 mInProfile = qcms_profile_from_memory(aData, aLength);
864 if (mInProfile) {
865 MOZ_LOG(sBMPLog, LogLevel::Debug, ("using embedded color profile\n"));
866 PrepareColorProfileTransform();
869 // Jump back to where we left off.
870 mIterator = std::move(mReturnIterator);
871 return Transition::To(State::ALLOCATE_SURFACE, 0);
874 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::AllocateSurface() {
875 SurfaceFormat format;
876 SurfacePipeFlags pipeFlags = SurfacePipeFlags();
878 if (mMayHaveTransparency) {
879 format = SurfaceFormat::OS_RGBA;
880 if (!(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA)) {
881 pipeFlags |= SurfacePipeFlags::PREMULTIPLY_ALPHA;
883 } else {
884 format = SurfaceFormat::OS_RGBX;
887 if (mH.mHeight >= 0) {
888 // BMPs store their rows in reverse order, so we may need to flip.
889 pipeFlags |= SurfacePipeFlags::FLIP_VERTICALLY;
892 mRowBuffer.reset(new (fallible) uint32_t[mH.mWidth]);
893 if (!mRowBuffer) {
894 return Transition::TerminateFailure();
897 // Only give the color transform to the SurfacePipe if we are not transforming
898 // the color table in advance.
899 qcms_transform* transform = mColors ? nullptr : mTransform;
901 Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
902 this, Size(), OutputSize(), FullFrame(), format, format, Nothing(),
903 transform, pipeFlags);
904 if (!pipe) {
905 return Transition::TerminateFailure();
908 mPipe = std::move(*pipe);
909 ClearRowBufferRemainder();
910 return Transition::To(State::COLOR_TABLE, mNumColors * mBytesPerColor);
913 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadColorTable(
914 const char* aData, size_t aLength) {
915 MOZ_ASSERT_IF(aLength != 0, mNumColors > 0 && mColors);
917 mPreGapLength += aLength;
919 for (uint32_t i = 0; i < mNumColors; i++) {
920 // The format is BGR or BGR0.
921 mColors[i].mBlue = uint8_t(aData[0]);
922 mColors[i].mGreen = uint8_t(aData[1]);
923 mColors[i].mRed = uint8_t(aData[2]);
924 aData += mBytesPerColor;
927 // If we have a color table and a transform, we can avoid transforming each
928 // pixel by doing the table in advance. We color manage every entry in the
929 // table, even if it is smaller in case the BMP is malformed and overruns
930 // its stated color range.
931 if (mColors && mTransform) {
932 qcms_transform_data(mTransform, mColors.get(), mColors.get(), 256);
935 // If we are decoding a BMP from the clipboard, we did not know the data
936 // offset in advance. It is just defined as after the header and color table.
937 if (mIsForClipboard) {
938 mH.mDataOffset += mPreGapLength;
941 // We know how many bytes we've read so far (mPreGapLength) and we know the
942 // offset of the pixel data (mH.mDataOffset), so we can determine the length
943 // of the gap (possibly zero) between the color table and the pixel data.
945 // If the gap is negative the file must be malformed (e.g. mH.mDataOffset
946 // points into the middle of the color palette instead of past the end) and
947 // we give up.
948 if (mPreGapLength > mH.mDataOffset) {
949 return Transition::TerminateFailure();
952 uint32_t gapLength = mH.mDataOffset - mPreGapLength;
954 return Transition::ToUnbuffered(State::AFTER_GAP, State::GAP, gapLength);
957 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::SkipGap() {
958 return Transition::ContinueUnbuffered(State::GAP);
961 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::AfterGap() {
962 // If there are no pixels we can stop.
964 // XXX: normally, if there are no pixels we will have stopped decoding before
965 // now, outside of this decoder. However, if the BMP is within an ICO file,
966 // it's possible that the ICO claimed the image had a non-zero size while the
967 // BMP claims otherwise. This test is to catch that awkward case. If we ever
968 // come up with a more general solution to this ICO-and-BMP-disagree-on-size
969 // problem, this test can be removed.
970 if (mH.mWidth == 0 || mH.mHeight == 0) {
971 return Transition::TerminateSuccess();
974 bool hasRLE = mH.mCompression == Compression::RLE8 ||
975 mH.mCompression == Compression::RLE4;
976 return hasRLE ? Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH)
977 : Transition::To(State::PIXEL_ROW, mPixelRowSize);
980 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadPixelRow(
981 const char* aData) {
982 MOZ_ASSERT(mCurrentRow > 0);
983 MOZ_ASSERT(mCurrentPos == 0);
985 const uint8_t* src = reinterpret_cast<const uint8_t*>(aData);
986 uint32_t* dst = RowBuffer();
987 uint32_t lpos = mH.mWidth;
988 switch (mH.mBpp) {
989 case 1:
990 while (lpos > 0) {
991 int8_t bit;
992 uint8_t idx;
993 for (bit = 7; bit >= 0 && lpos > 0; bit--) {
994 idx = (*src >> bit) & 1;
995 SetPixel(dst, idx, mColors);
996 --lpos;
998 ++src;
1000 break;
1002 case 4:
1003 while (lpos > 0) {
1004 Set4BitPixel(dst, *src, lpos, mColors);
1005 ++src;
1007 break;
1009 case 8:
1010 while (lpos > 0) {
1011 SetPixel(dst, *src, mColors);
1012 --lpos;
1013 ++src;
1015 break;
1017 case 16:
1018 if (mBitFields.IsR5G5B5()) {
1019 // Specialize this common case.
1020 while (lpos > 0) {
1021 uint16_t val = LittleEndian::readUint16(src);
1022 SetPixel(dst, mBitFields.mRed.Get5(val), mBitFields.mGreen.Get5(val),
1023 mBitFields.mBlue.Get5(val));
1024 --lpos;
1025 src += 2;
1027 } else {
1028 bool anyHasAlpha = false;
1029 while (lpos > 0) {
1030 uint16_t val = LittleEndian::readUint16(src);
1031 SetPixel(dst, mBitFields.mRed.Get(val), mBitFields.mGreen.Get(val),
1032 mBitFields.mBlue.Get(val),
1033 mBitFields.mAlpha.GetAlpha(val, anyHasAlpha));
1034 --lpos;
1035 src += 2;
1037 if (anyHasAlpha) {
1038 MOZ_ASSERT(mMayHaveTransparency);
1039 mDoesHaveTransparency = true;
1042 break;
1044 case 24:
1045 while (lpos > 0) {
1046 SetPixel(dst, src[2], src[1], src[0]);
1047 --lpos;
1048 src += 3;
1050 break;
1052 case 32:
1053 if (mH.mCompression == Compression::RGB && mIsWithinICO &&
1054 mH.mBpp == 32) {
1055 // This is a special case only used for 32bpp WinBMPv3-ICO files, which
1056 // could be in either 0RGB or ARGB format. We start by assuming it's
1057 // an 0RGB image. If we hit a non-zero alpha value, then we know it's
1058 // actually an ARGB image, and change tack accordingly.
1059 // (Note: a fully-transparent ARGB image is indistinguishable from a
1060 // 0RGB image, and we will render such an image as a 0RGB image, i.e.
1061 // opaquely. This is unlikely to be a problem in practice.)
1062 while (lpos > 0) {
1063 if (!mDoesHaveTransparency && src[3] != 0) {
1064 // Up until now this looked like an 0RGB image, but we now know
1065 // it's actually an ARGB image. Which means every pixel we've seen
1066 // so far has been fully transparent. So we go back and redo them.
1068 // Tell the SurfacePipe to go back to the start.
1069 mPipe.ResetToFirstRow();
1071 // Redo the complete rows we've already done.
1072 MOZ_ASSERT(mCurrentPos == 0);
1073 int32_t currentRow = mCurrentRow;
1074 mCurrentRow = AbsoluteHeight();
1075 ClearRowBufferRemainder();
1076 while (mCurrentRow > currentRow) {
1077 FinishRow();
1080 // Reset the row pointer back to where we started.
1081 dst = RowBuffer() + (mH.mWidth - lpos);
1083 MOZ_ASSERT(mMayHaveTransparency);
1084 mDoesHaveTransparency = true;
1087 // If mDoesHaveTransparency is false, treat this as an 0RGB image.
1088 // Otherwise, treat this as an ARGB image.
1089 SetPixel(dst, src[2], src[1], src[0],
1090 mDoesHaveTransparency ? src[3] : 0xff);
1091 src += 4;
1092 --lpos;
1094 } else if (mBitFields.IsR8G8B8()) {
1095 // Specialize this common case.
1096 while (lpos > 0) {
1097 uint32_t val = LittleEndian::readUint32(src);
1098 SetPixel(dst, mBitFields.mRed.Get8(val), mBitFields.mGreen.Get8(val),
1099 mBitFields.mBlue.Get8(val));
1100 --lpos;
1101 src += 4;
1103 } else {
1104 bool anyHasAlpha = false;
1105 while (lpos > 0) {
1106 uint32_t val = LittleEndian::readUint32(src);
1107 SetPixel(dst, mBitFields.mRed.Get(val), mBitFields.mGreen.Get(val),
1108 mBitFields.mBlue.Get(val),
1109 mBitFields.mAlpha.GetAlpha(val, anyHasAlpha));
1110 --lpos;
1111 src += 4;
1113 if (anyHasAlpha) {
1114 MOZ_ASSERT(mMayHaveTransparency);
1115 mDoesHaveTransparency = true;
1118 break;
1120 default:
1121 MOZ_CRASH("Unsupported color depth; earlier check didn't catch it?");
1124 FinishRow();
1125 return mCurrentRow == 0 ? Transition::TerminateSuccess()
1126 : Transition::To(State::PIXEL_ROW, mPixelRowSize);
1129 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadRLESegment(
1130 const char* aData) {
1131 if (mCurrentRow == 0) {
1132 return Transition::TerminateSuccess();
1135 uint8_t byte1 = uint8_t(aData[0]);
1136 uint8_t byte2 = uint8_t(aData[1]);
1138 if (byte1 != RLE::ESCAPE) {
1139 // Encoded mode consists of two bytes: byte1 specifies the number of
1140 // consecutive pixels to be drawn using the color index contained in
1141 // byte2.
1143 // Work around bitmaps that specify too many pixels.
1144 uint32_t pixelsNeeded = std::min<uint32_t>(mH.mWidth - mCurrentPos, byte1);
1145 if (pixelsNeeded) {
1146 uint32_t* dst = RowBuffer();
1147 mCurrentPos += pixelsNeeded;
1148 if (mH.mCompression == Compression::RLE8) {
1149 do {
1150 SetPixel(dst, byte2, mColors);
1151 pixelsNeeded--;
1152 } while (pixelsNeeded);
1153 } else {
1154 do {
1155 Set4BitPixel(dst, byte2, pixelsNeeded, mColors);
1156 } while (pixelsNeeded);
1159 return Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1162 if (byte2 == RLE::ESCAPE_EOL) {
1163 ClearRowBufferRemainder();
1164 mCurrentPos = 0;
1165 FinishRow();
1166 return mCurrentRow == 0
1167 ? Transition::TerminateSuccess()
1168 : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1171 if (byte2 == RLE::ESCAPE_EOF) {
1172 return Transition::TerminateSuccess();
1175 if (byte2 == RLE::ESCAPE_DELTA) {
1176 return Transition::To(State::RLE_DELTA, RLE::DELTA_LENGTH);
1179 // Absolute mode. |byte2| gives the number of pixels. The length depends on
1180 // whether it's 4-bit or 8-bit RLE. Also, the length must be even (and zero
1181 // padding is used to achieve this when necessary).
1182 MOZ_ASSERT(mAbsoluteModeNumPixels == 0);
1183 mAbsoluteModeNumPixels = byte2;
1184 uint32_t length = byte2;
1185 if (mH.mCompression == Compression::RLE4) {
1186 length = (length + 1) / 2; // halve, rounding up
1188 if (length & 1) {
1189 length++;
1191 return Transition::To(State::RLE_ABSOLUTE, length);
1194 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadRLEDelta(
1195 const char* aData) {
1196 // Delta encoding makes it possible to skip pixels making part of the image
1197 // transparent.
1198 MOZ_ASSERT(mMayHaveTransparency);
1199 mDoesHaveTransparency = true;
1201 // Clear the skipped pixels. (This clears to the end of the row,
1202 // which is perfect if there's a Y delta and harmless if not).
1203 ClearRowBufferRemainder();
1205 // Handle the XDelta.
1206 mCurrentPos += uint8_t(aData[0]);
1207 if (mCurrentPos > mH.mWidth) {
1208 mCurrentPos = mH.mWidth;
1211 // Handle the Y Delta.
1212 int32_t yDelta = std::min<int32_t>(uint8_t(aData[1]), mCurrentRow);
1213 if (yDelta > 0) {
1214 // Commit the current row (the first of the skipped rows).
1215 FinishRow();
1217 // Clear and commit the remaining skipped rows. We want to be careful not
1218 // to change mCurrentPos here.
1219 memset(mRowBuffer.get(), 0, mH.mWidth * sizeof(uint32_t));
1220 for (int32_t line = 1; line < yDelta; line++) {
1221 FinishRow();
1225 return mCurrentRow == 0
1226 ? Transition::TerminateSuccess()
1227 : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1230 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadRLEAbsolute(
1231 const char* aData, size_t aLength) {
1232 uint32_t n = mAbsoluteModeNumPixels;
1233 mAbsoluteModeNumPixels = 0;
1235 if (mCurrentPos + n > uint32_t(mH.mWidth)) {
1236 // Some DIB RLE8 encoders count a padding byte as the absolute mode
1237 // pixel number at the end of the row.
1238 if (mH.mCompression == Compression::RLE8 && n > 0 && (n & 1) == 0 &&
1239 mCurrentPos + n - uint32_t(mH.mWidth) == 1 && aLength > 0 &&
1240 aData[aLength - 1] == 0) {
1241 n--;
1242 } else {
1243 // Bad data. Stop decoding; at least part of the image may have been
1244 // decoded.
1245 return Transition::TerminateSuccess();
1249 // In absolute mode, n represents the number of pixels that follow, each of
1250 // which contains the color index of a single pixel.
1251 uint32_t* dst = RowBuffer();
1252 uint32_t iSrc = 0;
1253 uint32_t* oldPos = dst;
1254 if (mH.mCompression == Compression::RLE8) {
1255 while (n > 0) {
1256 SetPixel(dst, aData[iSrc], mColors);
1257 n--;
1258 iSrc++;
1260 } else {
1261 while (n > 0) {
1262 Set4BitPixel(dst, aData[iSrc], n, mColors);
1263 iSrc++;
1266 mCurrentPos += dst - oldPos;
1268 // We should read all the data (unless the last byte is zero padding).
1269 MOZ_ASSERT(iSrc == aLength - 1 || iSrc == aLength);
1271 return Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1274 } // namespace image
1275 } // namespace mozilla