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"
104 #include "RasterImage.h"
105 #include "SurfacePipeFactory.h"
106 #include "gfxPlatform.h"
109 using namespace mozilla::gfx
;
116 enum { RGB
= 0, RLE8
= 1, RLE4
= 2, BITFIELDS
= 3 };
119 // RLE escape codes and constants.
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
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
,
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
);
187 SetPixel(aDecoded
, idx
, aColors
);
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
,
200 mLexer(Transition::To(aState
, aLength
), Transition::TerminateSuccess()),
202 mIsForClipboard(aForClipboard
),
203 mMayHaveTransparency(false),
204 mDoesHaveTransparency(false),
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
,
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) {
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
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()
247 nsresult
nsBMPDecoder::BeforeFinishInternal() {
248 if (!IsMetadataDecode() && !mImageData
) {
249 return NS_ERROR_FAILURE
; // No image; something went wrong.
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);
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|
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
);
297 // ----------------------------------------
298 // Actual Data Processing
299 // ----------------------------------------
301 void BitFields::Value::Set(uint32_t 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
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().
316 // Find the rightmost 1.
318 for (i
= 0; i
< 32; i
++) {
319 if (mMask
& (1 << 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
))) {
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
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
);
366 MOZ_ALWAYS_INLINE
uint8_t BitFields::Value::GetAlpha(uint32_t aValue
,
367 bool& aHasAlphaOut
) const {
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
;
387 void BitFields::SetR5G5B5() {
393 void BitFields::SetR8G8B8() {
399 bool BitFields::IsR5G5B5() const {
400 return mRed
.mBitWidth
== 5 && mGreen
.mBitWidth
== 5 && mBlue
.mBitWidth
== 5 &&
404 bool BitFields::IsR8G8B8() const {
405 return mRed
.mBitWidth
== 8 && mGreen
.mBitWidth
== 8 && mBlue
.mBitWidth
== 8 &&
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();
420 PostInvalidation(invalidRect
->mInputSpaceRect
,
421 Some(invalidRect
->mOutputSpaceRect
));
426 LexerResult
nsBMPDecoder::DoDecode(SourceBufferIterator
& aIterator
,
427 IResumable
* aOnResume
) {
428 MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
431 aIterator
, aOnResume
,
432 [=](State aState
, const char* aData
, size_t aLength
) {
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
);
455 case State::AFTER_GAP
:
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
);
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';
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
);
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);
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.
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
);
558 mH
.mCsType
= InfoColorSpace::SRGB
;
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);
568 mH
.mCsType
= InfoColorSpace::SRGB
;
571 case InfoColorSpace::LINKED
:
572 case InfoColorSpace::SRGB
:
573 case InfoColorSpace::WIN
:
575 // Nothing to be done at this time.
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.
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, "
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;
604 0 <= mH
.mWidth
&& mH
.mWidth
<= k64KWidth
&& mH
.mHeight
!= INT_MIN
;
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()
633 mPixelRowSize
= (mH
.mBpp
* mH
.mWidth
+ 7) / 8;
634 uint32_t surplus
= mPixelRowSize
% 4;
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
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
;
654 // Bitfields are present after the info header, so we will read them in
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));
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().
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());
701 return Transition::TerminateFailure();
704 // We've now read all the headers. If we're doing a metadata decode, we're
706 if (IsMetadataDecode()) {
707 return Transition::TerminateSuccess();
710 // Set up the color table, if present; it'll be filled in by ReadColorTable().
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();
733 case InfoColorSpace::SRGB
:
734 case InfoColorSpace::WIN
:
735 MOZ_LOG(sBMPLog
, LogLevel::Debug
, ("using sRGB color profile\n"));
737 // We will transform the color table instead of the output pixels.
738 mTransform
= GetCMSsRGBTransform(SurfaceFormat::R8G8B8
);
740 mTransform
= GetCMSsRGBTransform(SurfaceFormat::OS_RGBA
);
743 case InfoColorSpace::LINKED
:
745 // Not supported, no color management.
746 MOZ_LOG(sBMPLog
, LogLevel::Debug
, ("color space type not provided\n"));
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
;
761 CalRbgEndpointToQcms(mH
.mColorSpace
.mCalibrated
.mRed
, primaries
.red
);
763 CalRbgEndpointToQcms(mH
.mColorSpace
.mCalibrated
.mGreen
, primaries
.green
);
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;
779 MOZ_LOG(sBMPLog
, LogLevel::Debug
, ("using calibrated RGB color profile\n"));
780 PrepareColorProfileTransform();
782 MOZ_LOG(sBMPLog
, LogLevel::Debug
,
783 ("failed to create calibrated RGB color profile, using sRGB\n"));
785 // We will transform the color table instead of the output pixels.
786 mTransform
= GetCMSsRGBTransform(SurfaceFormat::R8G8B8
);
788 mTransform
= GetCMSsRGBTransform(SurfaceFormat::OS_RGBA
);
793 void nsBMPDecoder::PrepareColorProfileTransform() {
794 if (!mInProfile
|| !GetCMSOutputProfile()) {
798 qcms_data_type inType
;
799 qcms_data_type outType
;
801 // We will transform the color table instead of the output pixels.
802 inType
= QCMS_DATA_RGB_8
;
803 outType
= QCMS_DATA_RGB_8
;
805 inType
= gfxPlatform::GetCMSOSRGBAType();
810 switch (mH
.mCsIntent
) {
811 case InfoColorIntent::BUSINESS
:
812 intent
= QCMS_INTENT_SATURATION
;
814 case InfoColorIntent::GRAPHICS
:
815 intent
= QCMS_INTENT_RELATIVE_COLORIMETRIC
;
817 case InfoColorIntent::ABS_COLORIMETRIC
:
818 intent
= QCMS_INTENT_ABSOLUTE_COLORIMETRIC
;
820 case InfoColorIntent::IMAGES
:
822 intent
= QCMS_INTENT_PERCEPTUAL
;
826 mTransform
= qcms_transform_create(mInProfile
, inType
, GetCMSOutputProfile(),
829 MOZ_LOG(sBMPLog
, LogLevel::Debug
,
830 ("failed to create color profile transform\n"));
834 LexerTransition
<nsBMPDecoder::State
> nsBMPDecoder::SeekColorProfile(
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
);
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
;
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
]);
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
);
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
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(
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
;
989 for (bit
= 7; bit
>= 0 && lpos
> 0; bit
--) {
990 idx
= (*src
>> bit
) & 1;
991 SetPixel(dst
, idx
, mColors
);
1000 Set4BitPixel(dst
, *src
, lpos
, mColors
);
1007 SetPixel(dst
, *src
, mColors
);
1014 if (mBitFields
.IsR5G5B5()) {
1015 // Specialize this common case.
1017 uint16_t val
= LittleEndian::readUint16(src
);
1018 SetPixel(dst
, mBitFields
.mRed
.Get5(val
), mBitFields
.mGreen
.Get5(val
),
1019 mBitFields
.mBlue
.Get5(val
));
1024 bool anyHasAlpha
= false;
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
));
1034 MOZ_ASSERT(mMayHaveTransparency
);
1035 mDoesHaveTransparency
= true;
1042 SetPixel(dst
, src
[2], src
[1], src
[0]);
1049 if (mH
.mCompression
== Compression::RGB
&& mIsWithinICO
&&
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.)
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
) {
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);
1090 } else if (mBitFields
.IsR8G8B8()) {
1091 // Specialize this common case.
1093 uint32_t val
= LittleEndian::readUint32(src
);
1094 SetPixel(dst
, mBitFields
.mRed
.Get8(val
), mBitFields
.mGreen
.Get8(val
),
1095 mBitFields
.mBlue
.Get8(val
));
1100 bool anyHasAlpha
= false;
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
));
1110 MOZ_ASSERT(mMayHaveTransparency
);
1111 mDoesHaveTransparency
= true;
1117 MOZ_CRASH("Unsupported color depth; earlier check didn't catch it?");
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
1139 // Work around bitmaps that specify too many pixels.
1140 uint32_t pixelsNeeded
= std::min
<uint32_t>(mH
.mWidth
- mCurrentPos
, byte1
);
1142 uint32_t* dst
= RowBuffer();
1143 mCurrentPos
+= pixelsNeeded
;
1144 if (mH
.mCompression
== Compression::RLE8
) {
1146 SetPixel(dst
, byte2
, mColors
);
1148 } while (pixelsNeeded
);
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();
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
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
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
);
1210 // Commit the current row (the first of the skipped rows).
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
++) {
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) {
1239 // Bad data. Stop decoding; at least part of the image may have been
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();
1249 uint32_t* oldPos
= dst
;
1250 if (mH
.mCompression
== Compression::RLE8
) {
1252 SetPixel(dst
, aData
[iSrc
], mColors
);
1258 Set4BitPixel(dst
, aData
[iSrc
], n
, mColors
);
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