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());
702 return Transition::TerminateFailure();
705 // We've now read all the headers. If we're doing a metadata decode, we're
707 if (IsMetadataDecode()) {
708 return Transition::TerminateSuccess();
711 // Set up the color table, if present; it'll be filled in by ReadColorTable().
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();
737 case InfoColorSpace::SRGB
:
738 case InfoColorSpace::WIN
:
739 MOZ_LOG(sBMPLog
, LogLevel::Debug
, ("using sRGB color profile\n"));
741 // We will transform the color table instead of the output pixels.
742 mTransform
= GetCMSsRGBTransform(SurfaceFormat::R8G8B8
);
744 mTransform
= GetCMSsRGBTransform(SurfaceFormat::OS_RGBA
);
747 case InfoColorSpace::LINKED
:
749 // Not supported, no color management.
750 MOZ_LOG(sBMPLog
, LogLevel::Debug
, ("color space type not provided\n"));
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
;
765 CalRbgEndpointToQcms(mH
.mColorSpace
.mCalibrated
.mRed
, primaries
.red
);
767 CalRbgEndpointToQcms(mH
.mColorSpace
.mCalibrated
.mGreen
, primaries
.green
);
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;
783 MOZ_LOG(sBMPLog
, LogLevel::Debug
, ("using calibrated RGB color profile\n"));
784 PrepareColorProfileTransform();
786 MOZ_LOG(sBMPLog
, LogLevel::Debug
,
787 ("failed to create calibrated RGB color profile, using sRGB\n"));
789 // We will transform the color table instead of the output pixels.
790 mTransform
= GetCMSsRGBTransform(SurfaceFormat::R8G8B8
);
792 mTransform
= GetCMSsRGBTransform(SurfaceFormat::OS_RGBA
);
797 void nsBMPDecoder::PrepareColorProfileTransform() {
798 if (!mInProfile
|| !GetCMSOutputProfile()) {
802 qcms_data_type inType
;
803 qcms_data_type outType
;
805 // We will transform the color table instead of the output pixels.
806 inType
= QCMS_DATA_RGB_8
;
807 outType
= QCMS_DATA_RGB_8
;
809 inType
= gfxPlatform::GetCMSOSRGBAType();
814 switch (mH
.mCsIntent
) {
815 case InfoColorIntent::BUSINESS
:
816 intent
= QCMS_INTENT_SATURATION
;
818 case InfoColorIntent::GRAPHICS
:
819 intent
= QCMS_INTENT_RELATIVE_COLORIMETRIC
;
821 case InfoColorIntent::ABS_COLORIMETRIC
:
822 intent
= QCMS_INTENT_ABSOLUTE_COLORIMETRIC
;
824 case InfoColorIntent::IMAGES
:
826 intent
= QCMS_INTENT_PERCEPTUAL
;
830 mTransform
= qcms_transform_create(mInProfile
, inType
, GetCMSOutputProfile(),
833 MOZ_LOG(sBMPLog
, LogLevel::Debug
,
834 ("failed to create color profile transform\n"));
838 LexerTransition
<nsBMPDecoder::State
> nsBMPDecoder::SeekColorProfile(
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
);
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
;
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
]);
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
);
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
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(
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
;
993 for (bit
= 7; bit
>= 0 && lpos
> 0; bit
--) {
994 idx
= (*src
>> bit
) & 1;
995 SetPixel(dst
, idx
, mColors
);
1004 Set4BitPixel(dst
, *src
, lpos
, mColors
);
1011 SetPixel(dst
, *src
, mColors
);
1018 if (mBitFields
.IsR5G5B5()) {
1019 // Specialize this common case.
1021 uint16_t val
= LittleEndian::readUint16(src
);
1022 SetPixel(dst
, mBitFields
.mRed
.Get5(val
), mBitFields
.mGreen
.Get5(val
),
1023 mBitFields
.mBlue
.Get5(val
));
1028 bool anyHasAlpha
= false;
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
));
1038 MOZ_ASSERT(mMayHaveTransparency
);
1039 mDoesHaveTransparency
= true;
1046 SetPixel(dst
, src
[2], src
[1], src
[0]);
1053 if (mH
.mCompression
== Compression::RGB
&& mIsWithinICO
&&
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.)
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
) {
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);
1094 } else if (mBitFields
.IsR8G8B8()) {
1095 // Specialize this common case.
1097 uint32_t val
= LittleEndian::readUint32(src
);
1098 SetPixel(dst
, mBitFields
.mRed
.Get8(val
), mBitFields
.mGreen
.Get8(val
),
1099 mBitFields
.mBlue
.Get8(val
));
1104 bool anyHasAlpha
= false;
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
));
1114 MOZ_ASSERT(mMayHaveTransparency
);
1115 mDoesHaveTransparency
= true;
1121 MOZ_CRASH("Unsupported color depth; earlier check didn't catch it?");
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
1143 // Work around bitmaps that specify too many pixels.
1144 uint32_t pixelsNeeded
= std::min
<uint32_t>(mH
.mWidth
- mCurrentPos
, byte1
);
1146 uint32_t* dst
= RowBuffer();
1147 mCurrentPos
+= pixelsNeeded
;
1148 if (mH
.mCompression
== Compression::RLE8
) {
1150 SetPixel(dst
, byte2
, mColors
);
1152 } while (pixelsNeeded
);
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();
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
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
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
);
1214 // Commit the current row (the first of the skipped rows).
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
++) {
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) {
1243 // Bad data. Stop decoding; at least part of the image may have been
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();
1253 uint32_t* oldPos
= dst
;
1254 if (mH
.mCompression
== Compression::RLE8
) {
1256 SetPixel(dst
, aData
[iSrc
], mColors
);
1262 Set4BitPixel(dst
, aData
[iSrc
], n
, mColors
);
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