Bug 1815313 - Add test for invalid schemes in onLoadRequest. r=jonalmeida
[gecko.git] / image / decoders / nsBMPDecoder.cpp
blob5eea76ea149eaf64df9b938dd85baa430e3296f5
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"
104 #include "RasterImage.h"
105 #include "SurfacePipeFactory.h"
106 #include "gfxPlatform.h"
107 #include <algorithm>
109 using namespace mozilla::gfx;
111 namespace mozilla {
112 namespace image {
113 namespace bmp {
115 struct Compression {
116 enum { RGB = 0, RLE8 = 1, RLE4 = 2, BITFIELDS = 3 };
119 // RLE escape codes and constants.
120 struct RLE {
121 enum {
122 ESCAPE = 0,
123 ESCAPE_EOL = 0,
124 ESCAPE_EOF = 1,
125 ESCAPE_DELTA = 2,
127 SEGMENT_LENGTH = 2,
128 DELTA_LENGTH = 2
132 } // namespace bmp
134 using namespace bmp;
136 static double FixedPoint2Dot30_To_Double(uint32_t aFixed) {
137 constexpr double factor = 1.0 / 1073741824.0; // 2^-30
138 return double(aFixed) * factor;
141 static float FixedPoint16Dot16_To_Float(uint32_t aFixed) {
142 constexpr double factor = 1.0 / 65536.0; // 2^-16
143 return double(aFixed) * factor;
146 static float CalRbgEndpointToQcms(const CalRgbEndpoint& aIn,
147 qcms_CIE_xyY& aOut) {
148 aOut.x = FixedPoint2Dot30_To_Double(aIn.mX);
149 aOut.y = FixedPoint2Dot30_To_Double(aIn.mY);
150 aOut.Y = FixedPoint2Dot30_To_Double(aIn.mZ);
151 return FixedPoint16Dot16_To_Float(aIn.mGamma);
154 static void ReadCalRgbEndpoint(const char* aData, uint32_t aEndpointOffset,
155 uint32_t aGammaOffset, CalRgbEndpoint& aOut) {
156 aOut.mX = LittleEndian::readUint32(aData + aEndpointOffset);
157 aOut.mY = LittleEndian::readUint32(aData + aEndpointOffset + 4);
158 aOut.mZ = LittleEndian::readUint32(aData + aEndpointOffset + 8);
159 aOut.mGamma = LittleEndian::readUint32(aData + aGammaOffset);
162 /// Sets the pixel data in aDecoded to the given values.
163 /// @param aDecoded pointer to pixel to be set, will be incremented to point to
164 /// the next pixel.
165 static void SetPixel(uint32_t*& aDecoded, uint8_t aRed, uint8_t aGreen,
166 uint8_t aBlue, uint8_t aAlpha = 0xFF) {
167 *aDecoded++ = gfxPackedPixelNoPreMultiply(aAlpha, aRed, aGreen, aBlue);
170 static void SetPixel(uint32_t*& aDecoded, uint8_t idx,
171 const UniquePtr<ColorTableEntry[]>& aColors) {
172 SetPixel(aDecoded, aColors[idx].mRed, aColors[idx].mGreen,
173 aColors[idx].mBlue);
176 /// Sets two (or one if aCount = 1) pixels
177 /// @param aDecoded where the data is stored. Will be moved 4 resp 8 bytes
178 /// depending on whether one or two pixels are written.
179 /// @param aData The values for the two pixels
180 /// @param aCount Current count. Is decremented by one or two.
181 static void Set4BitPixel(uint32_t*& aDecoded, uint8_t aData, uint32_t& aCount,
182 const UniquePtr<ColorTableEntry[]>& aColors) {
183 uint8_t idx = aData >> 4;
184 SetPixel(aDecoded, idx, aColors);
185 if (--aCount > 0) {
186 idx = aData & 0xF;
187 SetPixel(aDecoded, idx, aColors);
188 --aCount;
192 static mozilla::LazyLogModule sBMPLog("BMPDecoder");
194 // The length of the mBIHSize field in the info header.
195 static const uint32_t BIHSIZE_FIELD_LENGTH = 4;
197 nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, State aState, size_t aLength,
198 bool aForClipboard)
199 : Decoder(aImage),
200 mLexer(Transition::To(aState, aLength), Transition::TerminateSuccess()),
201 mIsWithinICO(false),
202 mIsForClipboard(aForClipboard),
203 mMayHaveTransparency(false),
204 mDoesHaveTransparency(false),
205 mNumColors(0),
206 mColors(nullptr),
207 mBytesPerColor(0),
208 mPreGapLength(0),
209 mPixelRowSize(0),
210 mCurrentRow(0),
211 mCurrentPos(0),
212 mAbsoluteModeNumPixels(0) {}
214 // Constructor for normal BMP files or from the clipboard.
215 nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, bool aForClipboard)
216 : nsBMPDecoder(aImage,
217 aForClipboard ? State::INFO_HEADER_SIZE : State::FILE_HEADER,
218 aForClipboard ? BIHSIZE_FIELD_LENGTH : FILE_HEADER_LENGTH,
219 aForClipboard) {}
221 // Constructor used for WinBMPv3-ICO files, which lack a file header.
222 nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, uint32_t aDataOffset)
223 : nsBMPDecoder(aImage, State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH,
224 /* aForClipboard */ false) {
225 SetIsWithinICO();
227 // Even though the file header isn't present in this case, the dataOffset
228 // field is set as if it is, and so we must increment mPreGapLength
229 // accordingly.
230 mPreGapLength += FILE_HEADER_LENGTH;
232 // This is the one piece of data we normally get from a BMP file header, so
233 // it must be provided via an argument.
234 mH.mDataOffset = aDataOffset;
237 nsBMPDecoder::~nsBMPDecoder() {}
239 // Obtains the size of the compressed image resource.
240 int32_t nsBMPDecoder::GetCompressedImageSize() const {
241 // In the RGB case mImageSize might not be set, so compute it manually.
242 MOZ_ASSERT(mPixelRowSize != 0);
243 return mH.mCompression == Compression::RGB ? mPixelRowSize * AbsoluteHeight()
244 : mH.mImageSize;
247 nsresult nsBMPDecoder::BeforeFinishInternal() {
248 if (!IsMetadataDecode() && !mImageData) {
249 return NS_ERROR_FAILURE; // No image; something went wrong.
252 return NS_OK;
255 nsresult nsBMPDecoder::FinishInternal() {
256 // We shouldn't be called in error cases.
257 MOZ_ASSERT(!HasError(), "Can't call FinishInternal on error!");
259 // We should never make multiple frames.
260 MOZ_ASSERT(GetFrameCount() <= 1, "Multiple BMP frames?");
262 // Send notifications if appropriate.
263 if (!IsMetadataDecode() && HasSize()) {
264 // We should have image data.
265 MOZ_ASSERT(mImageData);
267 // If it was truncated, fill in the missing pixels as black.
268 while (mCurrentRow > 0) {
269 uint32_t* dst = RowBuffer();
270 while (mCurrentPos < mH.mWidth) {
271 SetPixel(dst, 0, 0, 0);
272 mCurrentPos++;
274 mCurrentPos = 0;
275 FinishRow();
278 MOZ_ASSERT_IF(mDoesHaveTransparency, mMayHaveTransparency);
280 // We have transparency if we either detected some in the image itself
281 // (i.e., |mDoesHaveTransparency| is true) or we're in an ICO, which could
282 // mean we have an AND mask that provides transparency (i.e., |mIsWithinICO|
283 // is true).
284 // XXX(seth): We can tell when we create the decoder if the AND mask is
285 // present, so we could be more precise about this.
286 const Opacity opacity = mDoesHaveTransparency || mIsWithinICO
287 ? Opacity::SOME_TRANSPARENCY
288 : Opacity::FULLY_OPAQUE;
290 PostFrameStop(opacity);
291 PostDecodeDone();
294 return NS_OK;
297 // ----------------------------------------
298 // Actual Data Processing
299 // ----------------------------------------
301 void BitFields::Value::Set(uint32_t aMask) {
302 mMask = aMask;
304 // Handle this exceptional case first. The chosen values don't matter
305 // (because a mask of zero will always give a value of zero) except that
306 // mBitWidth:
307 // - shouldn't be zero, because that would cause an infinite loop in Get();
308 // - shouldn't be 5 or 8, because that could cause a false positive match in
309 // IsR5G5B5() or IsR8G8B8().
310 if (mMask == 0x0) {
311 mRightShift = 0;
312 mBitWidth = 1;
313 return;
316 // Find the rightmost 1.
317 uint8_t i;
318 for (i = 0; i < 32; i++) {
319 if (mMask & (1 << i)) {
320 break;
323 mRightShift = i;
325 // Now find the leftmost 1 in the same run of 1s. (If there are multiple runs
326 // of 1s -- which isn't valid -- we'll behave as if only the lowest run was
327 // present, which seems reasonable.)
328 for (i = i + 1; i < 32; i++) {
329 if (!(mMask & (1 << i))) {
330 break;
333 mBitWidth = i - mRightShift;
336 MOZ_ALWAYS_INLINE uint8_t BitFields::Value::Get(uint32_t aValue) const {
337 // Extract the unscaled value.
338 uint32_t v = (aValue & mMask) >> mRightShift;
340 // Idea: to upscale v precisely we need to duplicate its bits, possibly
341 // repeatedly, possibly partially in the last case, from bit 7 down to bit 0
342 // in v2. For example:
344 // - mBitWidth=1: v2 = v<<7 | v<<6 | ... | v<<1 | v>>0 k -> kkkkkkkk
345 // - mBitWidth=2: v2 = v<<6 | v<<4 | v<<2 | v>>0 jk -> jkjkjkjk
346 // - mBitWidth=3: v2 = v<<5 | v<<2 | v>>1 ijk -> ijkijkij
347 // - mBitWidth=4: v2 = v<<4 | v>>0 hijk -> hijkhijk
348 // - mBitWidth=5: v2 = v<<3 | v>>2 ghijk -> ghijkghi
349 // - mBitWidth=6: v2 = v<<2 | v>>4 fghijk -> fghijkfg
350 // - mBitWidth=7: v2 = v<<1 | v>>6 efghijk -> efghijke
351 // - mBitWidth=8: v2 = v>>0 defghijk -> defghijk
352 // - mBitWidth=9: v2 = v>>1 cdefghijk -> cdefghij
353 // - mBitWidth=10: v2 = v>>2 bcdefghijk -> bcdefghi
354 // - mBitWidth=11: v2 = v>>3 abcdefghijk -> abcdefgh
355 // - etc.
357 uint8_t v2 = 0;
358 int32_t i; // must be a signed integer
359 for (i = 8 - mBitWidth; i > 0; i -= mBitWidth) {
360 v2 |= v << uint32_t(i);
362 v2 |= v >> uint32_t(-i);
363 return v2;
366 MOZ_ALWAYS_INLINE uint8_t BitFields::Value::GetAlpha(uint32_t aValue,
367 bool& aHasAlphaOut) const {
368 if (mMask == 0x0) {
369 return 0xff;
371 aHasAlphaOut = true;
372 return Get(aValue);
375 MOZ_ALWAYS_INLINE uint8_t BitFields::Value::Get5(uint32_t aValue) const {
376 MOZ_ASSERT(mBitWidth == 5);
377 uint32_t v = (aValue & mMask) >> mRightShift;
378 return (v << 3u) | (v >> 2u);
381 MOZ_ALWAYS_INLINE uint8_t BitFields::Value::Get8(uint32_t aValue) const {
382 MOZ_ASSERT(mBitWidth == 8);
383 uint32_t v = (aValue & mMask) >> mRightShift;
384 return v;
387 void BitFields::SetR5G5B5() {
388 mRed.Set(0x7c00);
389 mGreen.Set(0x03e0);
390 mBlue.Set(0x001f);
393 void BitFields::SetR8G8B8() {
394 mRed.Set(0xff0000);
395 mGreen.Set(0xff00);
396 mBlue.Set(0x00ff);
399 bool BitFields::IsR5G5B5() const {
400 return mRed.mBitWidth == 5 && mGreen.mBitWidth == 5 && mBlue.mBitWidth == 5 &&
401 mAlpha.mMask == 0x0;
404 bool BitFields::IsR8G8B8() const {
405 return mRed.mBitWidth == 8 && mGreen.mBitWidth == 8 && mBlue.mBitWidth == 8 &&
406 mAlpha.mMask == 0x0;
409 uint32_t* nsBMPDecoder::RowBuffer() { return mRowBuffer.get() + mCurrentPos; }
411 void nsBMPDecoder::ClearRowBufferRemainder() {
412 int32_t len = mH.mWidth - mCurrentPos;
413 memset(RowBuffer(), mMayHaveTransparency ? 0 : 0xFF, len * sizeof(uint32_t));
416 void nsBMPDecoder::FinishRow() {
417 mPipe.WriteBuffer(mRowBuffer.get());
418 Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect();
419 if (invalidRect) {
420 PostInvalidation(invalidRect->mInputSpaceRect,
421 Some(invalidRect->mOutputSpaceRect));
423 mCurrentRow--;
426 LexerResult nsBMPDecoder::DoDecode(SourceBufferIterator& aIterator,
427 IResumable* aOnResume) {
428 MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
430 return mLexer.Lex(
431 aIterator, aOnResume,
432 [=](State aState, const char* aData, size_t aLength) {
433 switch (aState) {
434 case State::FILE_HEADER:
435 return ReadFileHeader(aData, aLength);
436 case State::INFO_HEADER_SIZE:
437 return ReadInfoHeaderSize(aData, aLength);
438 case State::INFO_HEADER_REST:
439 return ReadInfoHeaderRest(aData, aLength);
440 case State::BITFIELDS:
441 return ReadBitfields(aData, aLength);
442 case State::SKIP_TO_COLOR_PROFILE:
443 return Transition::ContinueUnbuffered(State::SKIP_TO_COLOR_PROFILE);
444 case State::FOUND_COLOR_PROFILE:
445 return Transition::To(State::COLOR_PROFILE,
446 mH.mColorSpace.mProfile.mLength);
447 case State::COLOR_PROFILE:
448 return ReadColorProfile(aData, aLength);
449 case State::ALLOCATE_SURFACE:
450 return AllocateSurface();
451 case State::COLOR_TABLE:
452 return ReadColorTable(aData, aLength);
453 case State::GAP:
454 return SkipGap();
455 case State::AFTER_GAP:
456 return AfterGap();
457 case State::PIXEL_ROW:
458 return ReadPixelRow(aData);
459 case State::RLE_SEGMENT:
460 return ReadRLESegment(aData);
461 case State::RLE_DELTA:
462 return ReadRLEDelta(aData);
463 case State::RLE_ABSOLUTE:
464 return ReadRLEAbsolute(aData, aLength);
465 default:
466 MOZ_CRASH("Unknown State");
471 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadFileHeader(
472 const char* aData, size_t aLength) {
473 mPreGapLength += aLength;
475 bool signatureOk = aData[0] == 'B' && aData[1] == 'M';
476 if (!signatureOk) {
477 return Transition::TerminateFailure();
480 // We ignore the filesize (aData + 2) and reserved (aData + 6) fields.
482 mH.mDataOffset = LittleEndian::readUint32(aData + 10);
484 return Transition::To(State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH);
487 // We read the info header in two steps: (a) read the mBIHSize field to
488 // determine how long the header is; (b) read the rest of the header.
489 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadInfoHeaderSize(
490 const char* aData, size_t aLength) {
491 mH.mBIHSize = LittleEndian::readUint32(aData);
493 // Data offset can be wrong so fix it using the BIH size.
494 if (!mIsForClipboard && mH.mDataOffset < mPreGapLength + mH.mBIHSize) {
495 mH.mDataOffset = mPreGapLength + mH.mBIHSize;
498 mPreGapLength += aLength;
500 bool bihSizeOk = mH.mBIHSize == InfoHeaderLength::WIN_V2 ||
501 mH.mBIHSize == InfoHeaderLength::WIN_V3 ||
502 mH.mBIHSize == InfoHeaderLength::WIN_V4 ||
503 mH.mBIHSize == InfoHeaderLength::WIN_V5 ||
504 (mH.mBIHSize >= InfoHeaderLength::OS2_V2_MIN &&
505 mH.mBIHSize <= InfoHeaderLength::OS2_V2_MAX);
506 if (!bihSizeOk) {
507 return Transition::TerminateFailure();
509 // ICO BMPs must have a WinBMPv3 header. nsICODecoder should have already
510 // terminated decoding if this isn't the case.
511 MOZ_ASSERT_IF(mIsWithinICO, mH.mBIHSize == InfoHeaderLength::WIN_V3);
513 return Transition::To(State::INFO_HEADER_REST,
514 mH.mBIHSize - BIHSIZE_FIELD_LENGTH);
517 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadInfoHeaderRest(
518 const char* aData, size_t aLength) {
519 mPreGapLength += aLength;
521 // |mWidth| and |mHeight| may be signed (Windows) or unsigned (OS/2). We just
522 // read as unsigned because in practice that's good enough.
523 if (mH.mBIHSize == InfoHeaderLength::WIN_V2) {
524 mH.mWidth = LittleEndian::readUint16(aData + 0);
525 mH.mHeight = LittleEndian::readUint16(aData + 2);
526 // We ignore the planes (aData + 4) field; it should always be 1.
527 mH.mBpp = LittleEndian::readUint16(aData + 6);
528 } else {
529 mH.mWidth = LittleEndian::readUint32(aData + 0);
530 mH.mHeight = LittleEndian::readUint32(aData + 4);
531 // We ignore the planes (aData + 4) field; it should always be 1.
532 mH.mBpp = LittleEndian::readUint16(aData + 10);
534 // For OS2-BMPv2 the info header may be as little as 16 bytes, so be
535 // careful for these fields.
536 mH.mCompression = aLength >= 16 ? LittleEndian::readUint32(aData + 12) : 0;
537 mH.mImageSize = aLength >= 20 ? LittleEndian::readUint32(aData + 16) : 0;
538 // We ignore the xppm (aData + 20) and yppm (aData + 24) fields.
539 mH.mNumColors = aLength >= 32 ? LittleEndian::readUint32(aData + 28) : 0;
540 // We ignore the important_colors (aData + 36) field.
542 // Read color management properties we may need later.
543 mH.mCsType =
544 aLength >= 56
545 ? static_cast<InfoColorSpace>(LittleEndian::readUint32(aData + 52))
546 : InfoColorSpace::SRGB;
547 mH.mCsIntent = aLength >= 108 ? static_cast<InfoColorIntent>(
548 LittleEndian::readUint32(aData + 104))
549 : InfoColorIntent::IMAGES;
551 switch (mH.mCsType) {
552 case InfoColorSpace::CALIBRATED_RGB:
553 if (aLength >= 104) {
554 ReadCalRgbEndpoint(aData, 56, 92, mH.mColorSpace.mCalibrated.mRed);
555 ReadCalRgbEndpoint(aData, 68, 96, mH.mColorSpace.mCalibrated.mGreen);
556 ReadCalRgbEndpoint(aData, 80, 100, mH.mColorSpace.mCalibrated.mBlue);
557 } else {
558 mH.mCsType = InfoColorSpace::SRGB;
560 break;
561 case InfoColorSpace::EMBEDDED:
562 if (aLength >= 116) {
563 mH.mColorSpace.mProfile.mOffset =
564 LittleEndian::readUint32(aData + 108);
565 mH.mColorSpace.mProfile.mLength =
566 LittleEndian::readUint32(aData + 112);
567 } else {
568 mH.mCsType = InfoColorSpace::SRGB;
570 break;
571 case InfoColorSpace::LINKED:
572 case InfoColorSpace::SRGB:
573 case InfoColorSpace::WIN:
574 default:
575 // Nothing to be done at this time.
576 break;
579 // For WinBMPv4, WinBMPv5 and (possibly) OS2-BMPv2 there are additional
580 // fields in the info header which we ignore, with the possible exception
581 // of the color bitfields (see below).
584 // The height for BMPs embedded inside an ICO includes spaces for the AND
585 // mask even if it is not present, thus we need to adjust for that here.
586 if (mIsWithinICO) {
587 // XXX(seth): Should we really be writing the absolute value from
588 // the BIH below? Seems like this could be problematic for inverted BMPs.
589 mH.mHeight = abs(mH.mHeight) / 2;
592 // Run with MOZ_LOG=BMPDecoder:5 set to see this output.
593 MOZ_LOG(sBMPLog, LogLevel::Debug,
594 ("BMP: bihsize=%u, %d x %d, bpp=%u, compression=%u, colors=%u, "
595 "data-offset=%u\n",
596 mH.mBIHSize, mH.mWidth, mH.mHeight, uint32_t(mH.mBpp),
597 mH.mCompression, mH.mNumColors, mH.mDataOffset));
599 // BMPs with negative width are invalid. Also, reject extremely wide images
600 // to keep the math sane. And reject INT_MIN as a height because you can't
601 // get its absolute value (because -INT_MIN is one more than INT_MAX).
602 const int32_t k64KWidth = 0x0000FFFF;
603 bool sizeOk =
604 0 <= mH.mWidth && mH.mWidth <= k64KWidth && mH.mHeight != INT_MIN;
605 if (!sizeOk) {
606 return Transition::TerminateFailure();
609 // Check mBpp and mCompression.
610 bool bppCompressionOk =
611 (mH.mCompression == Compression::RGB &&
612 (mH.mBpp == 1 || mH.mBpp == 4 || mH.mBpp == 8 || mH.mBpp == 16 ||
613 mH.mBpp == 24 || mH.mBpp == 32)) ||
614 (mH.mCompression == Compression::RLE8 && mH.mBpp == 8) ||
615 (mH.mCompression == Compression::RLE4 && mH.mBpp == 4) ||
616 (mH.mCompression == Compression::BITFIELDS &&
617 // For BITFIELDS compression we require an exact match for one of the
618 // WinBMP BIH sizes; this clearly isn't an OS2 BMP.
619 (mH.mBIHSize == InfoHeaderLength::WIN_V3 ||
620 mH.mBIHSize == InfoHeaderLength::WIN_V4 ||
621 mH.mBIHSize == InfoHeaderLength::WIN_V5) &&
622 (mH.mBpp == 16 || mH.mBpp == 32));
623 if (!bppCompressionOk) {
624 return Transition::TerminateFailure();
627 // Initialize our current row to the top of the image.
628 mCurrentRow = AbsoluteHeight();
630 // Round it up to the nearest byte count, then pad to 4-byte boundary.
631 // Compute this even for a metadate decode because GetCompressedImageSize()
632 // relies on it.
633 mPixelRowSize = (mH.mBpp * mH.mWidth + 7) / 8;
634 uint32_t surplus = mPixelRowSize % 4;
635 if (surplus != 0) {
636 mPixelRowSize += 4 - surplus;
639 size_t bitFieldsLengthStillToRead = 0;
640 if (mH.mCompression == Compression::BITFIELDS) {
641 // Need to read bitfields.
642 if (mH.mBIHSize >= InfoHeaderLength::WIN_V4) {
643 // Bitfields are present in the info header, so we can read them
644 // immediately.
645 mBitFields.ReadFromHeader(aData + 36, /* aReadAlpha = */ true);
647 // If this came from the clipboard, then we know that even if the header
648 // explicitly includes the bitfield masks, we need to add an additional
649 // offset for the start of the RGB data.
650 if (mIsForClipboard) {
651 mH.mDataOffset += BitFields::LENGTH;
653 } else {
654 // Bitfields are present after the info header, so we will read them in
655 // ReadBitfields().
656 bitFieldsLengthStillToRead = BitFields::LENGTH;
658 } else if (mH.mBpp == 16) {
659 // No bitfields specified; use the default 5-5-5 values.
660 mBitFields.SetR5G5B5();
661 } else if (mH.mBpp == 32) {
662 // No bitfields specified; use the default 8-8-8 values.
663 mBitFields.SetR8G8B8();
666 return Transition::To(State::BITFIELDS, bitFieldsLengthStillToRead);
669 void BitFields::ReadFromHeader(const char* aData, bool aReadAlpha) {
670 mRed.Set(LittleEndian::readUint32(aData + 0));
671 mGreen.Set(LittleEndian::readUint32(aData + 4));
672 mBlue.Set(LittleEndian::readUint32(aData + 8));
673 if (aReadAlpha) {
674 mAlpha.Set(LittleEndian::readUint32(aData + 12));
678 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadBitfields(
679 const char* aData, size_t aLength) {
680 mPreGapLength += aLength;
682 // If aLength is zero there are no bitfields to read, or we already read them
683 // in ReadInfoHeader().
684 if (aLength != 0) {
685 mBitFields.ReadFromHeader(aData, /* aReadAlpha = */ false);
688 // Note that RLE-encoded BMPs might be transparent because the 'delta' mode
689 // can skip pixels and cause implicit transparency.
690 mMayHaveTransparency = mIsWithinICO || mH.mCompression == Compression::RLE8 ||
691 mH.mCompression == Compression::RLE4 ||
692 (mH.mCompression == Compression::BITFIELDS &&
693 mBitFields.mAlpha.IsPresent());
694 if (mMayHaveTransparency) {
695 PostHasTransparency();
698 // Post our size to the superclass.
699 PostSize(mH.mWidth, AbsoluteHeight());
700 if (HasError()) {
701 return Transition::TerminateFailure();
704 // We've now read all the headers. If we're doing a metadata decode, we're
705 // done.
706 if (IsMetadataDecode()) {
707 return Transition::TerminateSuccess();
710 // Set up the color table, if present; it'll be filled in by ReadColorTable().
711 if (mH.mBpp <= 8) {
712 mNumColors = 1 << mH.mBpp;
713 if (0 < mH.mNumColors && mH.mNumColors < mNumColors) {
714 mNumColors = mH.mNumColors;
717 // Always allocate and zero 256 entries, even though mNumColors might be
718 // smaller, because the file might erroneously index past mNumColors.
719 mColors = MakeUnique<ColorTableEntry[]>(256);
720 memset(mColors.get(), 0, 256 * sizeof(ColorTableEntry));
722 // OS/2 Bitmaps have no padding byte.
723 mBytesPerColor = (mH.mBIHSize == InfoHeaderLength::WIN_V2) ? 3 : 4;
726 if (mCMSMode != CMSMode::Off) {
727 switch (mH.mCsType) {
728 case InfoColorSpace::EMBEDDED:
729 return SeekColorProfile(aLength);
730 case InfoColorSpace::CALIBRATED_RGB:
731 PrepareCalibratedColorProfile();
732 break;
733 case InfoColorSpace::SRGB:
734 case InfoColorSpace::WIN:
735 MOZ_LOG(sBMPLog, LogLevel::Debug, ("using sRGB color profile\n"));
736 if (mColors) {
737 // We will transform the color table instead of the output pixels.
738 mTransform = GetCMSsRGBTransform(SurfaceFormat::R8G8B8);
739 } else {
740 mTransform = GetCMSsRGBTransform(SurfaceFormat::OS_RGBA);
742 break;
743 case InfoColorSpace::LINKED:
744 default:
745 // Not supported, no color management.
746 MOZ_LOG(sBMPLog, LogLevel::Debug, ("color space type not provided\n"));
747 break;
751 return Transition::To(State::ALLOCATE_SURFACE, 0);
754 void nsBMPDecoder::PrepareCalibratedColorProfile() {
755 // BMP does not define a white point. Use the same as sRGB. This matches what
756 // Chrome does as well.
757 qcms_CIE_xyY white_point = qcms_white_point_sRGB();
759 qcms_CIE_xyYTRIPLE primaries;
760 float redGamma =
761 CalRbgEndpointToQcms(mH.mColorSpace.mCalibrated.mRed, primaries.red);
762 float greenGamma =
763 CalRbgEndpointToQcms(mH.mColorSpace.mCalibrated.mGreen, primaries.green);
764 float blueGamma =
765 CalRbgEndpointToQcms(mH.mColorSpace.mCalibrated.mBlue, primaries.blue);
767 // Explicitly verify the profile because sometimes the values from the BMP
768 // header are just garbage.
769 mInProfile = qcms_profile_create_rgb_with_gamma_set(
770 white_point, primaries, redGamma, greenGamma, blueGamma);
771 if (mInProfile && qcms_profile_is_bogus(mInProfile)) {
772 // Bad profile, just use sRGB instead. Release the profile here, so that
773 // our destructor doesn't assume we are the owner for the transform.
774 qcms_profile_release(mInProfile);
775 mInProfile = nullptr;
778 if (mInProfile) {
779 MOZ_LOG(sBMPLog, LogLevel::Debug, ("using calibrated RGB color profile\n"));
780 PrepareColorProfileTransform();
781 } else {
782 MOZ_LOG(sBMPLog, LogLevel::Debug,
783 ("failed to create calibrated RGB color profile, using sRGB\n"));
784 if (mColors) {
785 // We will transform the color table instead of the output pixels.
786 mTransform = GetCMSsRGBTransform(SurfaceFormat::R8G8B8);
787 } else {
788 mTransform = GetCMSsRGBTransform(SurfaceFormat::OS_RGBA);
793 void nsBMPDecoder::PrepareColorProfileTransform() {
794 if (!mInProfile || !GetCMSOutputProfile()) {
795 return;
798 qcms_data_type inType;
799 qcms_data_type outType;
800 if (mColors) {
801 // We will transform the color table instead of the output pixels.
802 inType = QCMS_DATA_RGB_8;
803 outType = QCMS_DATA_RGB_8;
804 } else {
805 inType = gfxPlatform::GetCMSOSRGBAType();
806 outType = inType;
809 qcms_intent intent;
810 switch (mH.mCsIntent) {
811 case InfoColorIntent::BUSINESS:
812 intent = QCMS_INTENT_SATURATION;
813 break;
814 case InfoColorIntent::GRAPHICS:
815 intent = QCMS_INTENT_RELATIVE_COLORIMETRIC;
816 break;
817 case InfoColorIntent::ABS_COLORIMETRIC:
818 intent = QCMS_INTENT_ABSOLUTE_COLORIMETRIC;
819 break;
820 case InfoColorIntent::IMAGES:
821 default:
822 intent = QCMS_INTENT_PERCEPTUAL;
823 break;
826 mTransform = qcms_transform_create(mInProfile, inType, GetCMSOutputProfile(),
827 outType, intent);
828 if (!mTransform) {
829 MOZ_LOG(sBMPLog, LogLevel::Debug,
830 ("failed to create color profile transform\n"));
834 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::SeekColorProfile(
835 size_t aLength) {
836 // The offset needs to be at least after the color table.
837 uint32_t offset = mH.mColorSpace.mProfile.mOffset;
838 if (offset <= mH.mBIHSize + aLength + mNumColors * mBytesPerColor ||
839 mH.mColorSpace.mProfile.mLength == 0) {
840 return Transition::To(State::ALLOCATE_SURFACE, 0);
843 // We have already read the header and bitfields.
844 offset -= mH.mBIHSize + aLength;
846 // We need to skip ahead to search for the embedded color profile. We want
847 // to return to this point once we read it.
848 mReturnIterator = mLexer.Clone(*mIterator, SIZE_MAX);
849 if (!mReturnIterator) {
850 return Transition::TerminateFailure();
853 return Transition::ToUnbuffered(State::FOUND_COLOR_PROFILE,
854 State::SKIP_TO_COLOR_PROFILE, offset);
857 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadColorProfile(
858 const char* aData, size_t aLength) {
859 mInProfile = qcms_profile_from_memory(aData, aLength);
860 if (mInProfile) {
861 MOZ_LOG(sBMPLog, LogLevel::Debug, ("using embedded color profile\n"));
862 PrepareColorProfileTransform();
865 // Jump back to where we left off.
866 mIterator = std::move(mReturnIterator);
867 return Transition::To(State::ALLOCATE_SURFACE, 0);
870 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::AllocateSurface() {
871 SurfaceFormat format;
872 SurfacePipeFlags pipeFlags = SurfacePipeFlags();
874 if (mMayHaveTransparency) {
875 format = SurfaceFormat::OS_RGBA;
876 if (!(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA)) {
877 pipeFlags |= SurfacePipeFlags::PREMULTIPLY_ALPHA;
879 } else {
880 format = SurfaceFormat::OS_RGBX;
883 if (mH.mHeight >= 0) {
884 // BMPs store their rows in reverse order, so we may need to flip.
885 pipeFlags |= SurfacePipeFlags::FLIP_VERTICALLY;
888 mRowBuffer.reset(new (fallible) uint32_t[mH.mWidth]);
889 if (!mRowBuffer) {
890 return Transition::TerminateFailure();
893 // Only give the color transform to the SurfacePipe if we are not transforming
894 // the color table in advance.
895 qcms_transform* transform = mColors ? nullptr : mTransform;
897 Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
898 this, Size(), OutputSize(), FullFrame(), format, format, Nothing(),
899 transform, pipeFlags);
900 if (!pipe) {
901 return Transition::TerminateFailure();
904 mPipe = std::move(*pipe);
905 ClearRowBufferRemainder();
906 return Transition::To(State::COLOR_TABLE, mNumColors * mBytesPerColor);
909 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadColorTable(
910 const char* aData, size_t aLength) {
911 MOZ_ASSERT_IF(aLength != 0, mNumColors > 0 && mColors);
913 mPreGapLength += aLength;
915 for (uint32_t i = 0; i < mNumColors; i++) {
916 // The format is BGR or BGR0.
917 mColors[i].mBlue = uint8_t(aData[0]);
918 mColors[i].mGreen = uint8_t(aData[1]);
919 mColors[i].mRed = uint8_t(aData[2]);
920 aData += mBytesPerColor;
923 // If we have a color table and a transform, we can avoid transforming each
924 // pixel by doing the table in advance. We color manage every entry in the
925 // table, even if it is smaller in case the BMP is malformed and overruns
926 // its stated color range.
927 if (mColors && mTransform) {
928 qcms_transform_data(mTransform, mColors.get(), mColors.get(), 256);
931 // If we are decoding a BMP from the clipboard, we did not know the data
932 // offset in advance. It is just defined as after the header and color table.
933 if (mIsForClipboard) {
934 mH.mDataOffset += mPreGapLength;
937 // We know how many bytes we've read so far (mPreGapLength) and we know the
938 // offset of the pixel data (mH.mDataOffset), so we can determine the length
939 // of the gap (possibly zero) between the color table and the pixel data.
941 // If the gap is negative the file must be malformed (e.g. mH.mDataOffset
942 // points into the middle of the color palette instead of past the end) and
943 // we give up.
944 if (mPreGapLength > mH.mDataOffset) {
945 return Transition::TerminateFailure();
948 uint32_t gapLength = mH.mDataOffset - mPreGapLength;
950 return Transition::ToUnbuffered(State::AFTER_GAP, State::GAP, gapLength);
953 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::SkipGap() {
954 return Transition::ContinueUnbuffered(State::GAP);
957 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::AfterGap() {
958 // If there are no pixels we can stop.
960 // XXX: normally, if there are no pixels we will have stopped decoding before
961 // now, outside of this decoder. However, if the BMP is within an ICO file,
962 // it's possible that the ICO claimed the image had a non-zero size while the
963 // BMP claims otherwise. This test is to catch that awkward case. If we ever
964 // come up with a more general solution to this ICO-and-BMP-disagree-on-size
965 // problem, this test can be removed.
966 if (mH.mWidth == 0 || mH.mHeight == 0) {
967 return Transition::TerminateSuccess();
970 bool hasRLE = mH.mCompression == Compression::RLE8 ||
971 mH.mCompression == Compression::RLE4;
972 return hasRLE ? Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH)
973 : Transition::To(State::PIXEL_ROW, mPixelRowSize);
976 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadPixelRow(
977 const char* aData) {
978 MOZ_ASSERT(mCurrentRow > 0);
979 MOZ_ASSERT(mCurrentPos == 0);
981 const uint8_t* src = reinterpret_cast<const uint8_t*>(aData);
982 uint32_t* dst = RowBuffer();
983 uint32_t lpos = mH.mWidth;
984 switch (mH.mBpp) {
985 case 1:
986 while (lpos > 0) {
987 int8_t bit;
988 uint8_t idx;
989 for (bit = 7; bit >= 0 && lpos > 0; bit--) {
990 idx = (*src >> bit) & 1;
991 SetPixel(dst, idx, mColors);
992 --lpos;
994 ++src;
996 break;
998 case 4:
999 while (lpos > 0) {
1000 Set4BitPixel(dst, *src, lpos, mColors);
1001 ++src;
1003 break;
1005 case 8:
1006 while (lpos > 0) {
1007 SetPixel(dst, *src, mColors);
1008 --lpos;
1009 ++src;
1011 break;
1013 case 16:
1014 if (mBitFields.IsR5G5B5()) {
1015 // Specialize this common case.
1016 while (lpos > 0) {
1017 uint16_t val = LittleEndian::readUint16(src);
1018 SetPixel(dst, mBitFields.mRed.Get5(val), mBitFields.mGreen.Get5(val),
1019 mBitFields.mBlue.Get5(val));
1020 --lpos;
1021 src += 2;
1023 } else {
1024 bool anyHasAlpha = false;
1025 while (lpos > 0) {
1026 uint16_t val = LittleEndian::readUint16(src);
1027 SetPixel(dst, mBitFields.mRed.Get(val), mBitFields.mGreen.Get(val),
1028 mBitFields.mBlue.Get(val),
1029 mBitFields.mAlpha.GetAlpha(val, anyHasAlpha));
1030 --lpos;
1031 src += 2;
1033 if (anyHasAlpha) {
1034 MOZ_ASSERT(mMayHaveTransparency);
1035 mDoesHaveTransparency = true;
1038 break;
1040 case 24:
1041 while (lpos > 0) {
1042 SetPixel(dst, src[2], src[1], src[0]);
1043 --lpos;
1044 src += 3;
1046 break;
1048 case 32:
1049 if (mH.mCompression == Compression::RGB && mIsWithinICO &&
1050 mH.mBpp == 32) {
1051 // This is a special case only used for 32bpp WinBMPv3-ICO files, which
1052 // could be in either 0RGB or ARGB format. We start by assuming it's
1053 // an 0RGB image. If we hit a non-zero alpha value, then we know it's
1054 // actually an ARGB image, and change tack accordingly.
1055 // (Note: a fully-transparent ARGB image is indistinguishable from a
1056 // 0RGB image, and we will render such an image as a 0RGB image, i.e.
1057 // opaquely. This is unlikely to be a problem in practice.)
1058 while (lpos > 0) {
1059 if (!mDoesHaveTransparency && src[3] != 0) {
1060 // Up until now this looked like an 0RGB image, but we now know
1061 // it's actually an ARGB image. Which means every pixel we've seen
1062 // so far has been fully transparent. So we go back and redo them.
1064 // Tell the SurfacePipe to go back to the start.
1065 mPipe.ResetToFirstRow();
1067 // Redo the complete rows we've already done.
1068 MOZ_ASSERT(mCurrentPos == 0);
1069 int32_t currentRow = mCurrentRow;
1070 mCurrentRow = AbsoluteHeight();
1071 ClearRowBufferRemainder();
1072 while (mCurrentRow > currentRow) {
1073 FinishRow();
1076 // Reset the row pointer back to where we started.
1077 dst = RowBuffer() + (mH.mWidth - lpos);
1079 MOZ_ASSERT(mMayHaveTransparency);
1080 mDoesHaveTransparency = true;
1083 // If mDoesHaveTransparency is false, treat this as an 0RGB image.
1084 // Otherwise, treat this as an ARGB image.
1085 SetPixel(dst, src[2], src[1], src[0],
1086 mDoesHaveTransparency ? src[3] : 0xff);
1087 src += 4;
1088 --lpos;
1090 } else if (mBitFields.IsR8G8B8()) {
1091 // Specialize this common case.
1092 while (lpos > 0) {
1093 uint32_t val = LittleEndian::readUint32(src);
1094 SetPixel(dst, mBitFields.mRed.Get8(val), mBitFields.mGreen.Get8(val),
1095 mBitFields.mBlue.Get8(val));
1096 --lpos;
1097 src += 4;
1099 } else {
1100 bool anyHasAlpha = false;
1101 while (lpos > 0) {
1102 uint32_t val = LittleEndian::readUint32(src);
1103 SetPixel(dst, mBitFields.mRed.Get(val), mBitFields.mGreen.Get(val),
1104 mBitFields.mBlue.Get(val),
1105 mBitFields.mAlpha.GetAlpha(val, anyHasAlpha));
1106 --lpos;
1107 src += 4;
1109 if (anyHasAlpha) {
1110 MOZ_ASSERT(mMayHaveTransparency);
1111 mDoesHaveTransparency = true;
1114 break;
1116 default:
1117 MOZ_CRASH("Unsupported color depth; earlier check didn't catch it?");
1120 FinishRow();
1121 return mCurrentRow == 0 ? Transition::TerminateSuccess()
1122 : Transition::To(State::PIXEL_ROW, mPixelRowSize);
1125 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadRLESegment(
1126 const char* aData) {
1127 if (mCurrentRow == 0) {
1128 return Transition::TerminateSuccess();
1131 uint8_t byte1 = uint8_t(aData[0]);
1132 uint8_t byte2 = uint8_t(aData[1]);
1134 if (byte1 != RLE::ESCAPE) {
1135 // Encoded mode consists of two bytes: byte1 specifies the number of
1136 // consecutive pixels to be drawn using the color index contained in
1137 // byte2.
1139 // Work around bitmaps that specify too many pixels.
1140 uint32_t pixelsNeeded = std::min<uint32_t>(mH.mWidth - mCurrentPos, byte1);
1141 if (pixelsNeeded) {
1142 uint32_t* dst = RowBuffer();
1143 mCurrentPos += pixelsNeeded;
1144 if (mH.mCompression == Compression::RLE8) {
1145 do {
1146 SetPixel(dst, byte2, mColors);
1147 pixelsNeeded--;
1148 } while (pixelsNeeded);
1149 } else {
1150 do {
1151 Set4BitPixel(dst, byte2, pixelsNeeded, mColors);
1152 } while (pixelsNeeded);
1155 return Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1158 if (byte2 == RLE::ESCAPE_EOL) {
1159 ClearRowBufferRemainder();
1160 mCurrentPos = 0;
1161 FinishRow();
1162 return mCurrentRow == 0
1163 ? Transition::TerminateSuccess()
1164 : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1167 if (byte2 == RLE::ESCAPE_EOF) {
1168 return Transition::TerminateSuccess();
1171 if (byte2 == RLE::ESCAPE_DELTA) {
1172 return Transition::To(State::RLE_DELTA, RLE::DELTA_LENGTH);
1175 // Absolute mode. |byte2| gives the number of pixels. The length depends on
1176 // whether it's 4-bit or 8-bit RLE. Also, the length must be even (and zero
1177 // padding is used to achieve this when necessary).
1178 MOZ_ASSERT(mAbsoluteModeNumPixels == 0);
1179 mAbsoluteModeNumPixels = byte2;
1180 uint32_t length = byte2;
1181 if (mH.mCompression == Compression::RLE4) {
1182 length = (length + 1) / 2; // halve, rounding up
1184 if (length & 1) {
1185 length++;
1187 return Transition::To(State::RLE_ABSOLUTE, length);
1190 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadRLEDelta(
1191 const char* aData) {
1192 // Delta encoding makes it possible to skip pixels making part of the image
1193 // transparent.
1194 MOZ_ASSERT(mMayHaveTransparency);
1195 mDoesHaveTransparency = true;
1197 // Clear the skipped pixels. (This clears to the end of the row,
1198 // which is perfect if there's a Y delta and harmless if not).
1199 ClearRowBufferRemainder();
1201 // Handle the XDelta.
1202 mCurrentPos += uint8_t(aData[0]);
1203 if (mCurrentPos > mH.mWidth) {
1204 mCurrentPos = mH.mWidth;
1207 // Handle the Y Delta.
1208 int32_t yDelta = std::min<int32_t>(uint8_t(aData[1]), mCurrentRow);
1209 if (yDelta > 0) {
1210 // Commit the current row (the first of the skipped rows).
1211 FinishRow();
1213 // Clear and commit the remaining skipped rows. We want to be careful not
1214 // to change mCurrentPos here.
1215 memset(mRowBuffer.get(), 0, mH.mWidth * sizeof(uint32_t));
1216 for (int32_t line = 1; line < yDelta; line++) {
1217 FinishRow();
1221 return mCurrentRow == 0
1222 ? Transition::TerminateSuccess()
1223 : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1226 LexerTransition<nsBMPDecoder::State> nsBMPDecoder::ReadRLEAbsolute(
1227 const char* aData, size_t aLength) {
1228 uint32_t n = mAbsoluteModeNumPixels;
1229 mAbsoluteModeNumPixels = 0;
1231 if (mCurrentPos + n > uint32_t(mH.mWidth)) {
1232 // Some DIB RLE8 encoders count a padding byte as the absolute mode
1233 // pixel number at the end of the row.
1234 if (mH.mCompression == Compression::RLE8 && n > 0 && (n & 1) == 0 &&
1235 mCurrentPos + n - uint32_t(mH.mWidth) == 1 && aLength > 0 &&
1236 aData[aLength - 1] == 0) {
1237 n--;
1238 } else {
1239 // Bad data. Stop decoding; at least part of the image may have been
1240 // decoded.
1241 return Transition::TerminateSuccess();
1245 // In absolute mode, n represents the number of pixels that follow, each of
1246 // which contains the color index of a single pixel.
1247 uint32_t* dst = RowBuffer();
1248 uint32_t iSrc = 0;
1249 uint32_t* oldPos = dst;
1250 if (mH.mCompression == Compression::RLE8) {
1251 while (n > 0) {
1252 SetPixel(dst, aData[iSrc], mColors);
1253 n--;
1254 iSrc++;
1256 } else {
1257 while (n > 0) {
1258 Set4BitPixel(dst, aData[iSrc], n, mColors);
1259 iSrc++;
1262 mCurrentPos += dst - oldPos;
1264 // We should read all the data (unless the last byte is zero padding).
1265 MOZ_ASSERT(iSrc == aLength - 1 || iSrc == aLength);
1267 return Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1270 } // namespace image
1271 } // namespace mozilla