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.
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 // ----------------------------------
24 // - This version is no longer used and can be ignored.
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
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.
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.
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 // -------------------------------
82 // - Almost identical to WinBMPv2; the differences are basically ignorable.
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
90 // - Also adds compression types "Huffman 1D" and "RLE24", which we don't
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"
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"
110 using namespace mozilla::gfx
;
117 enum { RGB
= 0, RLE8
= 1, RLE4
= 2, BITFIELDS
= 3 };
120 // RLE escape codes and constants.
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
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
,
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
);
188 SetPixel(aDecoded
, idx
, aColors
);
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
,
201 mLexer(Transition::To(aState
, aLength
), Transition::TerminateSuccess()),
203 mIsForClipboard(aForClipboard
),
204 mMayHaveTransparency(false),
205 mDoesHaveTransparency(false),
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
,
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) {
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
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()
248 nsresult
nsBMPDecoder::BeforeFinishInternal() {
249 if (!IsMetadataDecode() && !mImageData
) {
250 return NS_ERROR_FAILURE
; // No image; something went wrong.
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);
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|
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
);
298 // ----------------------------------------
299 // Actual Data Processing
300 // ----------------------------------------
302 void BitFields::Value::Set(uint32_t 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
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().
317 // Find the rightmost 1.
319 for (i
= 0; i
< 32; i
++) {
320 if (mMask
& (1 << 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
))) {
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
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
);
367 MOZ_ALWAYS_INLINE
uint8_t BitFields::Value::GetAlpha(uint32_t aValue
,
368 bool& aHasAlphaOut
) const {
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
;
388 void BitFields::SetR5G5B5() {
394 void BitFields::SetR8G8B8() {
400 bool BitFields::IsR5G5B5() const {
401 return mRed
.mBitWidth
== 5 && mGreen
.mBitWidth
== 5 && mBlue
.mBitWidth
== 5 &&
405 bool BitFields::IsR8G8B8() const {
406 return mRed
.mBitWidth
== 8 && mGreen
.mBitWidth
== 8 && mBlue
.mBitWidth
== 8 &&
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();
421 PostInvalidation(invalidRect
->mInputSpaceRect
,
422 Some(invalidRect
->mOutputSpaceRect
));
427 LexerResult
nsBMPDecoder::DoDecode(SourceBufferIterator
& aIterator
,
428 IResumable
* aOnResume
) {
429 MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
432 aIterator
, aOnResume
,
433 [=](State aState
, const char* aData
, size_t aLength
) {
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
);
456 case State::AFTER_GAP
:
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
);
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';
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
);
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);
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.
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
);
559 mH
.mCsType
= InfoColorSpace::SRGB
;
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);
569 mH
.mCsType
= InfoColorSpace::SRGB
;
572 case InfoColorSpace::LINKED
:
573 case InfoColorSpace::SRGB
:
574 case InfoColorSpace::WIN
:
576 // Nothing to be done at this time.
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.
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, "
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;
605 0 <= mH
.mWidth
&& mH
.mWidth
<= k64KWidth
&& mH
.mHeight
!= INT_MIN
;
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()
634 mPixelRowSize
= (mH
.mBpp
* mH
.mWidth
+ 7) / 8;
635 uint32_t surplus
= mPixelRowSize
% 4;
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
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
;
655 // Bitfields are present after the info header, so we will read them in
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));
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().
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);
705 return Transition::TerminateFailure();
708 // We've now read all the headers. If we're doing a metadata decode, we're
710 if (IsMetadataDecode()) {
711 return Transition::TerminateSuccess();
714 // Set up the color table, if present; it'll be filled in by ReadColorTable().
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();
740 case InfoColorSpace::SRGB
:
741 case InfoColorSpace::WIN
:
742 MOZ_LOG(sBMPLog
, LogLevel::Debug
, ("using sRGB color profile\n"));
744 // We will transform the color table instead of the output pixels.
745 mTransform
= GetCMSsRGBTransform(SurfaceFormat::R8G8B8
);
747 mTransform
= GetCMSsRGBTransform(SurfaceFormat::OS_RGBA
);
750 case InfoColorSpace::LINKED
:
752 // Not supported, no color management.
753 MOZ_LOG(sBMPLog
, LogLevel::Debug
, ("color space type not provided\n"));
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
;
768 CalRbgEndpointToQcms(mH
.mColorSpace
.mCalibrated
.mRed
, primaries
.red
);
770 CalRbgEndpointToQcms(mH
.mColorSpace
.mCalibrated
.mGreen
, primaries
.green
);
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;
786 MOZ_LOG(sBMPLog
, LogLevel::Debug
, ("using calibrated RGB color profile\n"));
787 PrepareColorProfileTransform();
789 MOZ_LOG(sBMPLog
, LogLevel::Debug
,
790 ("failed to create calibrated RGB color profile, using sRGB\n"));
792 // We will transform the color table instead of the output pixels.
793 mTransform
= GetCMSsRGBTransform(SurfaceFormat::R8G8B8
);
795 mTransform
= GetCMSsRGBTransform(SurfaceFormat::OS_RGBA
);
800 void nsBMPDecoder::PrepareColorProfileTransform() {
801 if (!mInProfile
|| !GetCMSOutputProfile()) {
805 qcms_data_type inType
;
806 qcms_data_type outType
;
808 // We will transform the color table instead of the output pixels.
809 inType
= QCMS_DATA_RGB_8
;
810 outType
= QCMS_DATA_RGB_8
;
812 inType
= gfxPlatform::GetCMSOSRGBAType();
817 switch (mH
.mCsIntent
) {
818 case InfoColorIntent::BUSINESS
:
819 intent
= QCMS_INTENT_SATURATION
;
821 case InfoColorIntent::GRAPHICS
:
822 intent
= QCMS_INTENT_RELATIVE_COLORIMETRIC
;
824 case InfoColorIntent::ABS_COLORIMETRIC
:
825 intent
= QCMS_INTENT_ABSOLUTE_COLORIMETRIC
;
827 case InfoColorIntent::IMAGES
:
829 intent
= QCMS_INTENT_PERCEPTUAL
;
833 mTransform
= qcms_transform_create(mInProfile
, inType
, GetCMSOutputProfile(),
836 MOZ_LOG(sBMPLog
, LogLevel::Debug
,
837 ("failed to create color profile transform\n"));
841 LexerTransition
<nsBMPDecoder::State
> nsBMPDecoder::SeekColorProfile(
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
);
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
;
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
]);
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
);
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
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(
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
;
996 for (bit
= 7; bit
>= 0 && lpos
> 0; bit
--) {
997 idx
= (*src
>> bit
) & 1;
998 SetPixel(dst
, idx
, mColors
);
1007 Set4BitPixel(dst
, *src
, lpos
, mColors
);
1014 SetPixel(dst
, *src
, mColors
);
1021 if (mBitFields
.IsR5G5B5()) {
1022 // Specialize this common case.
1024 uint16_t val
= LittleEndian::readUint16(src
);
1025 SetPixel(dst
, mBitFields
.mRed
.Get5(val
), mBitFields
.mGreen
.Get5(val
),
1026 mBitFields
.mBlue
.Get5(val
));
1031 bool anyHasAlpha
= false;
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
));
1041 MOZ_ASSERT(mMayHaveTransparency
);
1042 mDoesHaveTransparency
= true;
1049 SetPixel(dst
, src
[2], src
[1], src
[0]);
1056 if (mH
.mCompression
== Compression::RGB
&& mIsWithinICO
&&
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.)
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
) {
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);
1097 } else if (mBitFields
.IsR8G8B8()) {
1098 // Specialize this common case.
1100 uint32_t val
= LittleEndian::readUint32(src
);
1101 SetPixel(dst
, mBitFields
.mRed
.Get8(val
), mBitFields
.mGreen
.Get8(val
),
1102 mBitFields
.mBlue
.Get8(val
));
1107 bool anyHasAlpha
= false;
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
));
1117 MOZ_ASSERT(mMayHaveTransparency
);
1118 mDoesHaveTransparency
= true;
1124 MOZ_CRASH("Unsupported color depth; earlier check didn't catch it?");
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
1146 // Work around bitmaps that specify too many pixels.
1147 uint32_t pixelsNeeded
= std::min
<uint32_t>(mH
.mWidth
- mCurrentPos
, byte1
);
1149 uint32_t* dst
= RowBuffer();
1150 mCurrentPos
+= pixelsNeeded
;
1151 if (mH
.mCompression
== Compression::RLE8
) {
1153 SetPixel(dst
, byte2
, mColors
);
1155 } while (pixelsNeeded
);
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();
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
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
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
);
1217 // Commit the current row (the first of the skipped rows).
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
++) {
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) {
1246 // Bad data. Stop decoding; at least part of the image may have been
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();
1256 uint32_t* oldPos
= dst
;
1257 if (mH
.mCompression
== Compression::RLE8
) {
1259 SetPixel(dst
, aData
[iSrc
], mColors
);
1265 Set4BitPixel(dst
, aData
[iSrc
], n
, mColors
);
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