LCOV - code coverage report
Current view: top level - image/decoders - nsBMPDecoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 449 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 36 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : // This is a cross-platform BMP Decoder, which should work everywhere,
       8             : // including big-endian machines like the PowerPC.
       9             : //
      10             : // BMP is a format that has been extended multiple times. To understand the
      11             : // decoder you need to understand this history. The summary of the history
      12             : // below was determined from the following documents.
      13             : //
      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
      20             : //
      21             : // WINDOWS VERSIONS OF THE BMP FORMAT
      22             : // ----------------------------------
      23             : // WinBMPv1.
      24             : // - This version is no longer used and can be ignored.
      25             : //
      26             : // WinBMPv2.
      27             : // - First is a 14 byte file header that includes: the magic number ("BM"),
      28             : //   file size, and offset to the pixel data (|mDataOffset|).
      29             : // - Next is a 12 byte info header which includes: the info header size
      30             : //   (mBIHSize), width, height, number of color planes, and bits-per-pixel
      31             : //   (|mBpp|) which must be 1, 4, 8 or 24.
      32             : // - Next is the semi-optional color table, which has length 2^|mBpp| and has 3
      33             : //   bytes per value (BGR). The color table is required if |mBpp| is 1, 4, or 8.
      34             : // - Next is an optional gap.
      35             : // - Next is the pixel data, which is pointed to by |mDataOffset|.
      36             : //
      37             : // WinBMPv3. This is the most widely used version.
      38             : // - It changed the info header to 40 bytes by taking the WinBMPv2 info
      39             : //   header, enlargening its width and height fields, and adding more fields
      40             : //   including: a compression type (|mCompression|) and number of colors
      41             : //   (|mNumColors|).
      42             : // - The semi-optional color table is now 4 bytes per value (BGR0), and its
      43             : //   length is |mNumColors|, or 2^|mBpp| if |mNumColors| is zero.
      44             : // - |mCompression| can be RGB (i.e. no compression), RLE4 (if |mBpp|==4) or
      45             : //   RLE8 (if |mBpp|==8) values.
      46             : //
      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.
      52             : //
      53             : // WinBMPv4.
      54             : // - It extended the info header to 108 bytes, including the 12 bytes of color
      55             : //   mask data from WinBMPv3-NT, plus alpha mask data, and also color-space and
      56             : //   gamma correction fields.
      57             : //
      58             : // WinBMPv5.
      59             : // - It extended the info header to 124 bytes, adding color profile data.
      60             : // - It also added an optional color profile table after the pixel data (and
      61             : //   another optional gap).
      62             : //
      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.
      68             : //
      69             : // OS/2 VERSIONS OF THE BMP FORMAT
      70             : // -------------------------------
      71             : // OS2-BMPv1.
      72             : // - Almost identical to WinBMPv2; the differences are basically ignorable.
      73             : //
      74             : // OS2-BMPv2.
      75             : // - Similar to WinBMPv3.
      76             : // - The info header is 64 bytes but can be reduced to as little as 16; any
      77             : //   omitted fields are treated as zero. The first 40 bytes of these fields are
      78             : //   nearly identical to the WinBMPv3 info header; the remaining 24 bytes are
      79             : //   different.
      80             : // - Also adds compression types "Huffman 1D" and "RLE24", which we don't
      81             : //   support.
      82             : // - We treat OS2-BMPv2 files as if they are WinBMPv3 (i.e. ignore the extra 24
      83             : //   bytes in the info header), which in practice is good enough.
      84             : 
      85             : #include "ImageLogging.h"
      86             : #include "nsBMPDecoder.h"
      87             : 
      88             : #include <stdlib.h>
      89             : 
      90             : #include "mozilla/Attributes.h"
      91             : #include "mozilla/EndianUtils.h"
      92             : #include "mozilla/Likely.h"
      93             : 
      94             : #include "nsIInputStream.h"
      95             : #include "RasterImage.h"
      96             : #include <algorithm>
      97             : 
      98             : using namespace mozilla::gfx;
      99             : 
     100             : namespace mozilla {
     101             : namespace image {
     102             : namespace bmp {
     103             : 
     104             : struct Compression {
     105             :   enum {
     106             :     RGB = 0,
     107             :     RLE8 = 1,
     108             :     RLE4 = 2,
     109             :     BITFIELDS = 3
     110             :   };
     111             : };
     112             : 
     113             : // RLE escape codes and constants.
     114             : struct RLE {
     115             :   enum {
     116             :     ESCAPE = 0,
     117             :     ESCAPE_EOL = 0,
     118             :     ESCAPE_EOF = 1,
     119             :     ESCAPE_DELTA = 2,
     120             : 
     121             :     SEGMENT_LENGTH = 2,
     122             :     DELTA_LENGTH = 2
     123             :   };
     124             : };
     125             : 
     126             : } // namespace bmp
     127             : 
     128             : using namespace bmp;
     129             : 
     130             : /// Sets the pixel data in aDecoded to the given values.
     131             : /// @param aDecoded pointer to pixel to be set, will be incremented to point to
     132             : /// the next pixel.
     133             : static void
     134           0 : SetPixel(uint32_t*& aDecoded, uint8_t aRed, uint8_t aGreen,
     135             :          uint8_t aBlue, uint8_t aAlpha = 0xFF)
     136             : {
     137           0 :   *aDecoded++ = gfxPackedPixel(aAlpha, aRed, aGreen, aBlue);
     138           0 : }
     139             : 
     140             : static void
     141           0 : SetPixel(uint32_t*& aDecoded, uint8_t idx,
     142             :          const UniquePtr<ColorTableEntry[]>& aColors)
     143             : {
     144           0 :   SetPixel(aDecoded,
     145           0 :            aColors[idx].mRed, aColors[idx].mGreen, aColors[idx].mBlue);
     146           0 : }
     147             : 
     148             : /// Sets two (or one if aCount = 1) pixels
     149             : /// @param aDecoded where the data is stored. Will be moved 4 resp 8 bytes
     150             : /// depending on whether one or two pixels are written.
     151             : /// @param aData The values for the two pixels
     152             : /// @param aCount Current count. Is decremented by one or two.
     153             : static void
     154           0 : Set4BitPixel(uint32_t*& aDecoded, uint8_t aData, uint32_t& aCount,
     155             :              const UniquePtr<ColorTableEntry[]>& aColors)
     156             : {
     157           0 :   uint8_t idx = aData >> 4;
     158           0 :   SetPixel(aDecoded, idx, aColors);
     159           0 :   if (--aCount > 0) {
     160           0 :     idx = aData & 0xF;
     161           0 :     SetPixel(aDecoded, idx, aColors);
     162           0 :     --aCount;
     163             :   }
     164           0 : }
     165             : 
     166             : static mozilla::LazyLogModule sBMPLog("BMPDecoder");
     167             : 
     168             : // The length of the mBIHSize field in the info header.
     169             : static const uint32_t BIHSIZE_FIELD_LENGTH = 4;
     170             : 
     171           0 : nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, State aState, size_t aLength)
     172             :   : Decoder(aImage)
     173           0 :   , mLexer(Transition::To(aState, aLength), Transition::TerminateSuccess())
     174             :   , mIsWithinICO(false)
     175             :   , mMayHaveTransparency(false)
     176             :   , mDoesHaveTransparency(false)
     177             :   , mNumColors(0)
     178             :   , mColors(nullptr)
     179             :   , mBytesPerColor(0)
     180             :   , mPreGapLength(0)
     181             :   , mPixelRowSize(0)
     182             :   , mCurrentRow(0)
     183             :   , mCurrentPos(0)
     184           0 :   , mAbsoluteModeNumPixels(0)
     185             : {
     186           0 : }
     187             : 
     188             : // Constructor for normal BMP files.
     189           0 : nsBMPDecoder::nsBMPDecoder(RasterImage* aImage)
     190           0 :   : nsBMPDecoder(aImage, State::FILE_HEADER, FILE_HEADER_LENGTH)
     191             : {
     192           0 : }
     193             : 
     194             : // Constructor used for WinBMPv3-ICO files, which lack a file header.
     195           0 : nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, uint32_t aDataOffset)
     196           0 :   : nsBMPDecoder(aImage, State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH)
     197             : {
     198           0 :   SetIsWithinICO();
     199             : 
     200             :   // Even though the file header isn't present in this case, the dataOffset
     201             :   // field is set as if it is, and so we must increment mPreGapLength
     202             :   // accordingly.
     203           0 :   mPreGapLength += FILE_HEADER_LENGTH;
     204             : 
     205             :   // This is the one piece of data we normally get from a BMP file header, so
     206             :   // it must be provided via an argument.
     207           0 :   mH.mDataOffset = aDataOffset;
     208           0 : }
     209             : 
     210           0 : nsBMPDecoder::~nsBMPDecoder()
     211             : {
     212           0 : }
     213             : 
     214             : // Obtains the size of the compressed image resource.
     215             : int32_t
     216           0 : nsBMPDecoder::GetCompressedImageSize() const
     217             : {
     218             :   // In the RGB case mImageSize might not be set, so compute it manually.
     219           0 :   MOZ_ASSERT(mPixelRowSize != 0);
     220           0 :   return mH.mCompression == Compression::RGB
     221           0 :        ? mPixelRowSize * AbsoluteHeight()
     222           0 :        : mH.mImageSize;
     223             : }
     224             : 
     225             : nsresult
     226           0 : nsBMPDecoder::BeforeFinishInternal()
     227             : {
     228           0 :   if (!IsMetadataDecode() && !mImageData) {
     229           0 :     return NS_ERROR_FAILURE;  // No image; something went wrong.
     230             :   }
     231             : 
     232           0 :   return NS_OK;
     233             : }
     234             : 
     235             : nsresult
     236           0 : nsBMPDecoder::FinishInternal()
     237             : {
     238             :   // We shouldn't be called in error cases.
     239           0 :   MOZ_ASSERT(!HasError(), "Can't call FinishInternal on error!");
     240             : 
     241             :   // We should never make multiple frames.
     242           0 :   MOZ_ASSERT(GetFrameCount() <= 1, "Multiple BMP frames?");
     243             : 
     244             :   // Send notifications if appropriate.
     245           0 :   if (!IsMetadataDecode() && HasSize()) {
     246             : 
     247             :     // We should have image data.
     248           0 :     MOZ_ASSERT(mImageData);
     249             : 
     250             :     // If it was truncated, fill in the missing pixels as black.
     251           0 :     while (mCurrentRow > 0) {
     252           0 :       uint32_t* dst = RowBuffer();
     253           0 :       while (mCurrentPos < mH.mWidth) {
     254           0 :         SetPixel(dst, 0, 0, 0);
     255           0 :         mCurrentPos++;
     256             :       }
     257           0 :       mCurrentPos = 0;
     258           0 :       FinishRow();
     259             :     }
     260             : 
     261             :     // Invalidate.
     262           0 :     nsIntRect r(0, 0, mH.mWidth, AbsoluteHeight());
     263           0 :     PostInvalidation(r);
     264             : 
     265           0 :     MOZ_ASSERT_IF(mDoesHaveTransparency, mMayHaveTransparency);
     266             : 
     267             :     // We have transparency if we either detected some in the image itself
     268             :     // (i.e., |mDoesHaveTransparency| is true) or we're in an ICO, which could
     269             :     // mean we have an AND mask that provides transparency (i.e., |mIsWithinICO|
     270             :     // is true).
     271             :     // XXX(seth): We can tell when we create the decoder if the AND mask is
     272             :     // present, so we could be more precise about this.
     273           0 :     const Opacity opacity = mDoesHaveTransparency || mIsWithinICO
     274           0 :                           ? Opacity::SOME_TRANSPARENCY
     275           0 :                           : Opacity::FULLY_OPAQUE;
     276             : 
     277           0 :     PostFrameStop(opacity);
     278           0 :     PostDecodeDone();
     279             :   }
     280             : 
     281           0 :   return NS_OK;
     282             : }
     283             : 
     284             : // ----------------------------------------
     285             : // Actual Data Processing
     286             : // ----------------------------------------
     287             : 
     288             : void
     289           0 : BitFields::Value::Set(uint32_t aMask)
     290             : {
     291           0 :   mMask = aMask;
     292             : 
     293             :   // Handle this exceptional case first. The chosen values don't matter
     294             :   // (because a mask of zero will always give a value of zero) except that
     295             :   // mBitWidth:
     296             :   // - shouldn't be zero, because that would cause an infinite loop in Get();
     297             :   // - shouldn't be 5 or 8, because that could cause a false positive match in
     298             :   //   IsR5G5B5() or IsR8G8B8().
     299           0 :   if (mMask == 0x0) {
     300           0 :     mRightShift = 0;
     301           0 :     mBitWidth = 1;
     302           0 :     return;
     303             :   }
     304             : 
     305             :   // Find the rightmost 1.
     306             :   uint8_t i;
     307           0 :   for (i = 0; i < 32; i++) {
     308           0 :     if (mMask & (1 << i)) {
     309           0 :       break;
     310             :     }
     311             :   }
     312           0 :   mRightShift = i;
     313             : 
     314             :   // Now find the leftmost 1 in the same run of 1s. (If there are multiple runs
     315             :   // of 1s -- which isn't valid -- we'll behave as if only the lowest run was
     316             :   // present, which seems reasonable.)
     317           0 :   for (i = i + 1; i < 32; i++) {
     318           0 :     if (!(mMask & (1 << i))) {
     319           0 :       break;
     320             :     }
     321             :   }
     322           0 :   mBitWidth = i - mRightShift;
     323             : }
     324             : 
     325             : MOZ_ALWAYS_INLINE uint8_t
     326           0 : BitFields::Value::Get(uint32_t aValue) const
     327             : {
     328             :   // Extract the unscaled value.
     329           0 :   uint32_t v = (aValue & mMask) >> mRightShift;
     330             : 
     331             :   // Idea: to upscale v precisely we need to duplicate its bits, possibly
     332             :   // repeatedly, possibly partially in the last case, from bit 7 down to bit 0
     333             :   // in v2. For example:
     334             :   //
     335             :   // - mBitWidth=1:  v2 = v<<7 | v<<6 | ... | v<<1 | v>>0     k -> kkkkkkkk
     336             :   // - mBitWidth=2:  v2 = v<<6 | v<<4 | v<<2 | v>>0          jk -> jkjkjkjk
     337             :   // - mBitWidth=3:  v2 = v<<5 | v<<2 | v>>1                ijk -> ijkijkij
     338             :   // - mBitWidth=4:  v2 = v<<4 | v>>0                      hijk -> hijkhijk
     339             :   // - mBitWidth=5:  v2 = v<<3 | v>>2                     ghijk -> ghijkghi
     340             :   // - mBitWidth=6:  v2 = v<<2 | v>>4                    fghijk -> fghijkfg
     341             :   // - mBitWidth=7:  v2 = v<<1 | v>>6                   efghijk -> efghijke
     342             :   // - mBitWidth=8:  v2 = v>>0                         defghijk -> defghijk
     343             :   // - mBitWidth=9:  v2 = v>>1                        cdefghijk -> cdefghij
     344             :   // - mBitWidth=10: v2 = v>>2                       bcdefghijk -> bcdefghi
     345             :   // - mBitWidth=11: v2 = v>>3                      abcdefghijk -> abcdefgh
     346             :   // - etc.
     347             :   //
     348           0 :   uint8_t v2 = 0;
     349             :   int32_t i;      // must be a signed integer
     350           0 :   for (i = 8 - mBitWidth; i > 0; i -= mBitWidth) {
     351           0 :     v2 |= v << uint32_t(i);
     352             :   }
     353           0 :   v2 |= v >> uint32_t(-i);
     354           0 :   return v2;
     355             : }
     356             : 
     357             : MOZ_ALWAYS_INLINE uint8_t
     358           0 : BitFields::Value::GetAlpha(uint32_t aValue, bool& aHasAlphaOut) const
     359             : {
     360           0 :   if (mMask == 0x0) {
     361           0 :     return 0xff;
     362             :   }
     363           0 :   aHasAlphaOut = true;
     364           0 :   return Get(aValue);
     365             : }
     366             : 
     367             : MOZ_ALWAYS_INLINE uint8_t
     368           0 : BitFields::Value::Get5(uint32_t aValue) const
     369             : {
     370           0 :   MOZ_ASSERT(mBitWidth == 5);
     371           0 :   uint32_t v = (aValue & mMask) >> mRightShift;
     372           0 :   return (v << 3u) | (v >> 2u);
     373             : }
     374             : 
     375             : MOZ_ALWAYS_INLINE uint8_t
     376           0 : BitFields::Value::Get8(uint32_t aValue) const
     377             : {
     378           0 :   MOZ_ASSERT(mBitWidth == 8);
     379           0 :   uint32_t v = (aValue & mMask) >> mRightShift;
     380           0 :   return v;
     381             : }
     382             : 
     383             : void
     384           0 : BitFields::SetR5G5B5()
     385             : {
     386           0 :   mRed.Set(0x7c00);
     387           0 :   mGreen.Set(0x03e0);
     388           0 :   mBlue.Set(0x001f);
     389           0 : }
     390             : 
     391             : void
     392           0 : BitFields::SetR8G8B8()
     393             : {
     394           0 :   mRed.Set(0xff0000);
     395           0 :   mGreen.Set(0xff00);
     396           0 :   mBlue.Set(0x00ff);
     397           0 : }
     398             : 
     399             : bool
     400           0 : BitFields::IsR5G5B5() const
     401             : {
     402           0 :   return mRed.mBitWidth == 5 &&
     403           0 :          mGreen.mBitWidth == 5 &&
     404           0 :          mBlue.mBitWidth == 5 &&
     405           0 :          mAlpha.mMask == 0x0;
     406             : }
     407             : 
     408             : bool
     409           0 : BitFields::IsR8G8B8() const
     410             : {
     411           0 :   return mRed.mBitWidth == 8 &&
     412           0 :          mGreen.mBitWidth == 8 &&
     413           0 :          mBlue.mBitWidth == 8 &&
     414           0 :          mAlpha.mMask == 0x0;
     415             : }
     416             : 
     417             : uint32_t*
     418           0 : nsBMPDecoder::RowBuffer()
     419             : {
     420           0 :   if (mDownscaler) {
     421           0 :     return reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer()) + mCurrentPos;
     422             :   }
     423             : 
     424             :   // Convert from row (1..mHeight) to absolute line (0..mHeight-1).
     425           0 :   int32_t line = (mH.mHeight < 0)
     426           0 :                ? -mH.mHeight - mCurrentRow
     427           0 :                : mCurrentRow - 1;
     428           0 :   int32_t offset = line * mH.mWidth + mCurrentPos;
     429           0 :   return reinterpret_cast<uint32_t*>(mImageData) + offset;
     430             : }
     431             : 
     432             : void
     433           0 : nsBMPDecoder::FinishRow()
     434             : {
     435           0 :   if (mDownscaler) {
     436           0 :     mDownscaler->CommitRow();
     437             : 
     438           0 :     if (mDownscaler->HasInvalidation()) {
     439           0 :       DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
     440           0 :       PostInvalidation(invalidRect.mOriginalSizeRect,
     441           0 :                        Some(invalidRect.mTargetSizeRect));
     442             :     }
     443             :   } else {
     444           0 :     PostInvalidation(IntRect(0, mCurrentRow, mH.mWidth, 1));
     445             :   }
     446           0 :   mCurrentRow--;
     447           0 : }
     448             : 
     449             : LexerResult
     450           0 : nsBMPDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
     451             : {
     452           0 :   MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
     453             : 
     454             :   return mLexer.Lex(aIterator, aOnResume,
     455           0 :                     [=](State aState, const char* aData, size_t aLength) {
     456           0 :     switch (aState) {
     457           0 :       case State::FILE_HEADER:      return ReadFileHeader(aData, aLength);
     458           0 :       case State::INFO_HEADER_SIZE: return ReadInfoHeaderSize(aData, aLength);
     459           0 :       case State::INFO_HEADER_REST: return ReadInfoHeaderRest(aData, aLength);
     460           0 :       case State::BITFIELDS:        return ReadBitfields(aData, aLength);
     461           0 :       case State::COLOR_TABLE:      return ReadColorTable(aData, aLength);
     462           0 :       case State::GAP:              return SkipGap();
     463           0 :       case State::AFTER_GAP:        return AfterGap();
     464           0 :       case State::PIXEL_ROW:        return ReadPixelRow(aData);
     465           0 :       case State::RLE_SEGMENT:      return ReadRLESegment(aData);
     466           0 :       case State::RLE_DELTA:        return ReadRLEDelta(aData);
     467           0 :       case State::RLE_ABSOLUTE:     return ReadRLEAbsolute(aData, aLength);
     468             :       default:
     469           0 :         MOZ_CRASH("Unknown State");
     470             :     }
     471           0 :   });
     472             : }
     473             : 
     474             : LexerTransition<nsBMPDecoder::State>
     475           0 : nsBMPDecoder::ReadFileHeader(const char* aData, size_t aLength)
     476             : {
     477           0 :   mPreGapLength += aLength;
     478             : 
     479           0 :   bool signatureOk = aData[0] == 'B' && aData[1] == 'M';
     480           0 :   if (!signatureOk) {
     481           0 :     return Transition::TerminateFailure();
     482             :   }
     483             : 
     484             :   // We ignore the filesize (aData + 2) and reserved (aData + 6) fields.
     485             : 
     486           0 :   mH.mDataOffset = LittleEndian::readUint32(aData + 10);
     487             : 
     488           0 :   return Transition::To(State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH);
     489             : }
     490             : 
     491             : // We read the info header in two steps: (a) read the mBIHSize field to
     492             : // determine how long the header is; (b) read the rest of the header.
     493             : LexerTransition<nsBMPDecoder::State>
     494           0 : nsBMPDecoder::ReadInfoHeaderSize(const char* aData, size_t aLength)
     495             : {
     496           0 :   mPreGapLength += aLength;
     497             : 
     498           0 :   mH.mBIHSize = LittleEndian::readUint32(aData);
     499             : 
     500           0 :   bool bihSizeOk = mH.mBIHSize == InfoHeaderLength::WIN_V2 ||
     501           0 :                    mH.mBIHSize == InfoHeaderLength::WIN_V3 ||
     502           0 :                    mH.mBIHSize == InfoHeaderLength::WIN_V4 ||
     503           0 :                    mH.mBIHSize == InfoHeaderLength::WIN_V5 ||
     504           0 :                    (mH.mBIHSize >= InfoHeaderLength::OS2_V2_MIN &&
     505           0 :                     mH.mBIHSize <= InfoHeaderLength::OS2_V2_MAX);
     506           0 :   if (!bihSizeOk) {
     507           0 :     return Transition::TerminateFailure();
     508             :   }
     509             :   // ICO BMPs must have a WinBMPv3 header. nsICODecoder should have already
     510             :   // terminated decoding if this isn't the case.
     511           0 :   MOZ_ASSERT_IF(mIsWithinICO, mH.mBIHSize == InfoHeaderLength::WIN_V3);
     512             : 
     513             :   return Transition::To(State::INFO_HEADER_REST,
     514           0 :                         mH.mBIHSize - BIHSIZE_FIELD_LENGTH);
     515             : }
     516             : 
     517             : LexerTransition<nsBMPDecoder::State>
     518           0 : nsBMPDecoder::ReadInfoHeaderRest(const char* aData, size_t aLength)
     519             : {
     520           0 :   mPreGapLength += aLength;
     521             : 
     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           0 :   if (mH.mBIHSize == InfoHeaderLength::WIN_V2) {
     525           0 :     mH.mWidth  = LittleEndian::readUint16(aData + 0);
     526           0 :     mH.mHeight = LittleEndian::readUint16(aData + 2);
     527             :     // We ignore the planes (aData + 4) field; it should always be 1.
     528           0 :     mH.mBpp    = LittleEndian::readUint16(aData + 6);
     529             :   } else {
     530           0 :     mH.mWidth  = LittleEndian::readUint32(aData + 0);
     531           0 :     mH.mHeight = LittleEndian::readUint32(aData + 4);
     532             :     // We ignore the planes (aData + 4) field; it should always be 1.
     533           0 :     mH.mBpp    = LittleEndian::readUint16(aData + 10);
     534             : 
     535             :     // For OS2-BMPv2 the info header may be as little as 16 bytes, so be
     536             :     // careful for these fields.
     537           0 :     mH.mCompression = aLength >= 16 ? LittleEndian::readUint32(aData + 12) : 0;
     538           0 :     mH.mImageSize   = aLength >= 20 ? LittleEndian::readUint32(aData + 16) : 0;
     539             :     // We ignore the xppm (aData + 20) and yppm (aData + 24) fields.
     540           0 :     mH.mNumColors   = aLength >= 32 ? LittleEndian::readUint32(aData + 28) : 0;
     541             :     // We ignore the important_colors (aData + 36) field.
     542             : 
     543             :     // For WinBMPv4, WinBMPv5 and (possibly) OS2-BMPv2 there are additional
     544             :     // fields in the info header which we ignore, with the possible exception
     545             :     // of the color bitfields (see below).
     546             :   }
     547             : 
     548             :   // Run with MOZ_LOG=BMPDecoder:5 set to see this output.
     549           0 :   MOZ_LOG(sBMPLog, LogLevel::Debug,
     550             :           ("BMP: bihsize=%u, %d x %d, bpp=%u, compression=%u, colors=%u\n",
     551             :           mH.mBIHSize, mH.mWidth, mH.mHeight, uint32_t(mH.mBpp),
     552             :           mH.mCompression, mH.mNumColors));
     553             : 
     554             :   // BMPs with negative width are invalid. Also, reject extremely wide images
     555             :   // to keep the math sane. And reject INT_MIN as a height because you can't
     556             :   // get its absolute value (because -INT_MIN is one more than INT_MAX).
     557           0 :   const int32_t k64KWidth = 0x0000FFFF;
     558           0 :   bool sizeOk = 0 <= mH.mWidth && mH.mWidth <= k64KWidth &&
     559           0 :                 mH.mHeight != INT_MIN;
     560           0 :   if (!sizeOk) {
     561           0 :     return Transition::TerminateFailure();
     562             :   }
     563             : 
     564             :   // Check mBpp and mCompression.
     565             :   bool bppCompressionOk =
     566           0 :     (mH.mCompression == Compression::RGB &&
     567           0 :       (mH.mBpp ==  1 || mH.mBpp ==  4 || mH.mBpp ==  8 ||
     568           0 :        mH.mBpp == 16 || mH.mBpp == 24 || mH.mBpp == 32)) ||
     569           0 :     (mH.mCompression == Compression::RLE8 && mH.mBpp == 8) ||
     570           0 :     (mH.mCompression == Compression::RLE4 && mH.mBpp == 4) ||
     571           0 :     (mH.mCompression == Compression::BITFIELDS &&
     572             :       // For BITFIELDS compression we require an exact match for one of the
     573             :       // WinBMP BIH sizes; this clearly isn't an OS2 BMP.
     574           0 :       (mH.mBIHSize == InfoHeaderLength::WIN_V3 ||
     575           0 :        mH.mBIHSize == InfoHeaderLength::WIN_V4 ||
     576           0 :        mH.mBIHSize == InfoHeaderLength::WIN_V5) &&
     577           0 :       (mH.mBpp == 16 || mH.mBpp == 32));
     578           0 :   if (!bppCompressionOk) {
     579           0 :     return Transition::TerminateFailure();
     580             :   }
     581             : 
     582             :   // Initialize our current row to the top of the image.
     583           0 :   mCurrentRow = AbsoluteHeight();
     584             : 
     585             :   // Round it up to the nearest byte count, then pad to 4-byte boundary.
     586             :   // Compute this even for a metadate decode because GetCompressedImageSize()
     587             :   // relies on it.
     588           0 :   mPixelRowSize = (mH.mBpp * mH.mWidth + 7) / 8;
     589           0 :   uint32_t surplus = mPixelRowSize % 4;
     590           0 :   if (surplus != 0) {
     591           0 :     mPixelRowSize += 4 - surplus;
     592             :   }
     593             : 
     594           0 :   size_t bitFieldsLengthStillToRead = 0;
     595           0 :   if (mH.mCompression == Compression::BITFIELDS) {
     596             :     // Need to read bitfields.
     597           0 :     if (mH.mBIHSize >= InfoHeaderLength::WIN_V4) {
     598             :       // Bitfields are present in the info header, so we can read them
     599             :       // immediately.
     600           0 :       mBitFields.ReadFromHeader(aData + 36, /* aReadAlpha = */ true);
     601             :     } else {
     602             :       // Bitfields are present after the info header, so we will read them in
     603             :       // ReadBitfields().
     604           0 :       bitFieldsLengthStillToRead = BitFields::LENGTH;
     605             :     }
     606           0 :   } else if (mH.mBpp == 16) {
     607             :     // No bitfields specified; use the default 5-5-5 values.
     608           0 :     mBitFields.SetR5G5B5();
     609           0 :   } else if (mH.mBpp == 32) {
     610             :     // No bitfields specified; use the default 8-8-8 values.
     611           0 :     mBitFields.SetR8G8B8();
     612             :   }
     613             : 
     614           0 :   return Transition::To(State::BITFIELDS, bitFieldsLengthStillToRead);
     615             : }
     616             : 
     617             : void
     618           0 : BitFields::ReadFromHeader(const char* aData, bool aReadAlpha)
     619             : {
     620           0 :   mRed.Set  (LittleEndian::readUint32(aData + 0));
     621           0 :   mGreen.Set(LittleEndian::readUint32(aData + 4));
     622           0 :   mBlue.Set (LittleEndian::readUint32(aData + 8));
     623           0 :   if (aReadAlpha) {
     624           0 :     mAlpha.Set(LittleEndian::readUint32(aData + 12));
     625             :   }
     626           0 : }
     627             : 
     628             : LexerTransition<nsBMPDecoder::State>
     629           0 : nsBMPDecoder::ReadBitfields(const char* aData, size_t aLength)
     630             : {
     631           0 :   mPreGapLength += aLength;
     632             : 
     633             :   // If aLength is zero there are no bitfields to read, or we already read them
     634             :   // in ReadInfoHeader().
     635           0 :   if (aLength != 0) {
     636           0 :     mBitFields.ReadFromHeader(aData, /* aReadAlpha = */ false);
     637             :   }
     638             : 
     639             :   // Note that RLE-encoded BMPs might be transparent because the 'delta' mode
     640             :   // can skip pixels and cause implicit transparency.
     641           0 :   mMayHaveTransparency =
     642           0 :     mIsWithinICO ||
     643           0 :     mH.mCompression == Compression::RLE8 ||
     644           0 :     mH.mCompression == Compression::RLE4 ||
     645           0 :     (mH.mCompression == Compression::BITFIELDS &&
     646           0 :      mBitFields.mAlpha.IsPresent());
     647           0 :   if (mMayHaveTransparency) {
     648           0 :     PostHasTransparency();
     649             :   }
     650             : 
     651             :   // Post our size to the superclass.
     652           0 :   PostSize(mH.mWidth, AbsoluteHeight());
     653             : 
     654             :   // We've now read all the headers. If we're doing a metadata decode, we're
     655             :   // done.
     656           0 :   if (IsMetadataDecode()) {
     657           0 :     return Transition::TerminateSuccess();
     658             :   }
     659             : 
     660             :   // Set up the color table, if present; it'll be filled in by ReadColorTable().
     661           0 :   if (mH.mBpp <= 8) {
     662           0 :     mNumColors = 1 << mH.mBpp;
     663           0 :     if (0 < mH.mNumColors && mH.mNumColors < mNumColors) {
     664           0 :       mNumColors = mH.mNumColors;
     665             :     }
     666             : 
     667             :     // Always allocate and zero 256 entries, even though mNumColors might be
     668             :     // smaller, because the file might erroneously index past mNumColors.
     669           0 :     mColors = MakeUnique<ColorTableEntry[]>(256);
     670           0 :     memset(mColors.get(), 0, 256 * sizeof(ColorTableEntry));
     671             : 
     672             :     // OS/2 Bitmaps have no padding byte.
     673           0 :     mBytesPerColor = (mH.mBIHSize == InfoHeaderLength::WIN_V2) ? 3 : 4;
     674             :   }
     675             : 
     676           0 :   MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
     677           0 :   nsresult rv = AllocateFrame(/* aFrameNum = */ 0, OutputSize(),
     678           0 :                               FullOutputFrame(),
     679           0 :                               mMayHaveTransparency ? SurfaceFormat::B8G8R8A8
     680           0 :                                                    : SurfaceFormat::B8G8R8X8);
     681           0 :   if (NS_FAILED(rv)) {
     682           0 :     return Transition::TerminateFailure();
     683             :   }
     684           0 :   MOZ_ASSERT(mImageData, "Should have a buffer now");
     685             : 
     686           0 :   if (mDownscaler) {
     687             :     // BMPs store their rows in reverse order, so the downscaler needs to
     688             :     // reverse them again when writing its output. Unless the height is
     689             :     // negative!
     690           0 :     rv = mDownscaler->BeginFrame(Size(), Nothing(),
     691           0 :                                  mImageData, mMayHaveTransparency,
     692           0 :                                  /* aFlipVertically = */ mH.mHeight >= 0);
     693           0 :     if (NS_FAILED(rv)) {
     694           0 :       return Transition::TerminateFailure();
     695             :     }
     696             :   }
     697             : 
     698           0 :   return Transition::To(State::COLOR_TABLE, mNumColors * mBytesPerColor);
     699             : }
     700             : 
     701             : LexerTransition<nsBMPDecoder::State>
     702           0 : nsBMPDecoder::ReadColorTable(const char* aData, size_t aLength)
     703             : {
     704           0 :   MOZ_ASSERT_IF(aLength != 0, mNumColors > 0 && mColors);
     705             : 
     706           0 :   mPreGapLength += aLength;
     707             : 
     708           0 :   for (uint32_t i = 0; i < mNumColors; i++) {
     709             :     // The format is BGR or BGR0.
     710           0 :     mColors[i].mBlue  = uint8_t(aData[0]);
     711           0 :     mColors[i].mGreen = uint8_t(aData[1]);
     712           0 :     mColors[i].mRed   = uint8_t(aData[2]);
     713           0 :     aData += mBytesPerColor;
     714             :   }
     715             : 
     716             :   // We know how many bytes we've read so far (mPreGapLength) and we know the
     717             :   // offset of the pixel data (mH.mDataOffset), so we can determine the length
     718             :   // of the gap (possibly zero) between the color table and the pixel data.
     719             :   //
     720             :   // If the gap is negative the file must be malformed (e.g. mH.mDataOffset
     721             :   // points into the middle of the color palette instead of past the end) and
     722             :   // we give up.
     723           0 :   if (mPreGapLength > mH.mDataOffset) {
     724           0 :     return Transition::TerminateFailure();
     725             :   }
     726             : 
     727           0 :   uint32_t gapLength = mH.mDataOffset - mPreGapLength;
     728           0 :   return Transition::ToUnbuffered(State::AFTER_GAP, State::GAP, gapLength);
     729             : }
     730             : 
     731             : LexerTransition<nsBMPDecoder::State>
     732           0 : nsBMPDecoder::SkipGap()
     733             : {
     734           0 :   return Transition::ContinueUnbuffered(State::GAP);
     735             : }
     736             : 
     737             : LexerTransition<nsBMPDecoder::State>
     738           0 : nsBMPDecoder::AfterGap()
     739             : {
     740             :   // If there are no pixels we can stop.
     741             :   //
     742             :   // XXX: normally, if there are no pixels we will have stopped decoding before
     743             :   // now, outside of this decoder. However, if the BMP is within an ICO file,
     744             :   // it's possible that the ICO claimed the image had a non-zero size while the
     745             :   // BMP claims otherwise. This test is to catch that awkward case. If we ever
     746             :   // come up with a more general solution to this ICO-and-BMP-disagree-on-size
     747             :   // problem, this test can be removed.
     748           0 :   if (mH.mWidth == 0 || mH.mHeight == 0) {
     749           0 :     return Transition::TerminateSuccess();
     750             :   }
     751             : 
     752           0 :   bool hasRLE = mH.mCompression == Compression::RLE8 ||
     753           0 :                 mH.mCompression == Compression::RLE4;
     754             :   return hasRLE
     755             :        ? Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH)
     756           0 :        : Transition::To(State::PIXEL_ROW, mPixelRowSize);
     757             : }
     758             : 
     759             : LexerTransition<nsBMPDecoder::State>
     760           0 : nsBMPDecoder::ReadPixelRow(const char* aData)
     761             : {
     762           0 :   MOZ_ASSERT(mCurrentRow > 0);
     763           0 :   MOZ_ASSERT(mCurrentPos == 0);
     764             : 
     765           0 :   const uint8_t* src = reinterpret_cast<const uint8_t*>(aData);
     766           0 :   uint32_t* dst = RowBuffer();
     767           0 :   uint32_t lpos = mH.mWidth;
     768           0 :   switch (mH.mBpp) {
     769             :     case 1:
     770           0 :       while (lpos > 0) {
     771             :         int8_t bit;
     772             :         uint8_t idx;
     773           0 :         for (bit = 7; bit >= 0 && lpos > 0; bit--) {
     774           0 :           idx = (*src >> bit) & 1;
     775           0 :           SetPixel(dst, idx, mColors);
     776           0 :           --lpos;
     777             :         }
     778           0 :         ++src;
     779             :       }
     780           0 :       break;
     781             : 
     782             :     case 4:
     783           0 :       while (lpos > 0) {
     784           0 :         Set4BitPixel(dst, *src, lpos, mColors);
     785           0 :         ++src;
     786             :       }
     787           0 :       break;
     788             : 
     789             :     case 8:
     790           0 :       while (lpos > 0) {
     791           0 :         SetPixel(dst, *src, mColors);
     792           0 :         --lpos;
     793           0 :         ++src;
     794             :       }
     795           0 :       break;
     796             : 
     797             :     case 16:
     798           0 :       if (mBitFields.IsR5G5B5()) {
     799             :         // Specialize this common case.
     800           0 :         while (lpos > 0) {
     801           0 :           uint16_t val = LittleEndian::readUint16(src);
     802           0 :           SetPixel(dst, mBitFields.mRed.Get5(val),
     803           0 :                         mBitFields.mGreen.Get5(val),
     804           0 :                         mBitFields.mBlue.Get5(val));
     805           0 :           --lpos;
     806           0 :           src += 2;
     807             :         }
     808             :       } else {
     809           0 :         bool anyHasAlpha = false;
     810           0 :         while (lpos > 0) {
     811           0 :           uint16_t val = LittleEndian::readUint16(src);
     812           0 :           SetPixel(dst, mBitFields.mRed.Get(val),
     813           0 :                         mBitFields.mGreen.Get(val),
     814           0 :                         mBitFields.mBlue.Get(val),
     815           0 :                         mBitFields.mAlpha.GetAlpha(val, anyHasAlpha));
     816           0 :           --lpos;
     817           0 :           src += 2;
     818             :         }
     819           0 :         if (anyHasAlpha) {
     820           0 :           MOZ_ASSERT(mMayHaveTransparency);
     821           0 :           mDoesHaveTransparency = true;
     822             :         }
     823             :       }
     824           0 :       break;
     825             : 
     826             :     case 24:
     827           0 :       while (lpos > 0) {
     828           0 :         SetPixel(dst, src[2], src[1], src[0]);
     829           0 :         --lpos;
     830           0 :         src += 3;
     831             :       }
     832           0 :       break;
     833             : 
     834             :     case 32:
     835           0 :       if (mH.mCompression == Compression::RGB && mIsWithinICO &&
     836           0 :           mH.mBpp == 32) {
     837             :         // This is a special case only used for 32bpp WinBMPv3-ICO files, which
     838             :         // could be in either 0RGB or ARGB format. We start by assuming it's
     839             :         // an 0RGB image. If we hit a non-zero alpha value, then we know it's
     840             :         // actually an ARGB image, and change tack accordingly.
     841             :         // (Note: a fully-transparent ARGB image is indistinguishable from a
     842             :         // 0RGB image, and we will render such an image as a 0RGB image, i.e.
     843             :         // opaquely. This is unlikely to be a problem in practice.)
     844           0 :         while (lpos > 0) {
     845           0 :           if (!mDoesHaveTransparency && src[3] != 0) {
     846             :             // Up until now this looked like an 0RGB image, but we now know
     847             :             // it's actually an ARGB image. Which means every pixel we've seen
     848             :             // so far has been fully transparent. So we go back and redo them.
     849             : 
     850             :             // Tell the Downscaler to go back to the start.
     851           0 :             if (mDownscaler) {
     852           0 :               mDownscaler->ResetForNextProgressivePass();
     853             :             }
     854             : 
     855             :             // Redo the complete rows we've already done.
     856           0 :             MOZ_ASSERT(mCurrentPos == 0);
     857           0 :             int32_t currentRow = mCurrentRow;
     858           0 :             mCurrentRow = AbsoluteHeight();
     859           0 :             while (mCurrentRow > currentRow) {
     860           0 :               dst = RowBuffer();
     861           0 :               for (int32_t i = 0; i < mH.mWidth; i++) {
     862           0 :                 SetPixel(dst, 0, 0, 0, 0);
     863             :               }
     864           0 :               FinishRow();
     865             :             }
     866             : 
     867             :             // Redo the part of this row we've already done.
     868           0 :             dst = RowBuffer();
     869           0 :             int32_t n = mH.mWidth - lpos;
     870           0 :             for (int32_t i = 0; i < n; i++) {
     871           0 :               SetPixel(dst, 0, 0, 0, 0);
     872             :             }
     873             : 
     874           0 :             MOZ_ASSERT(mMayHaveTransparency);
     875           0 :             mDoesHaveTransparency = true;
     876             :           }
     877             : 
     878             :           // If mDoesHaveTransparency is false, treat this as an 0RGB image.
     879             :           // Otherwise, treat this as an ARGB image.
     880           0 :           SetPixel(dst, src[2], src[1], src[0],
     881           0 :                    mDoesHaveTransparency ? src[3] : 0xff);
     882           0 :           src += 4;
     883           0 :           --lpos;
     884           0 :         }
     885           0 :       } else if (mBitFields.IsR8G8B8()) {
     886             :         // Specialize this common case.
     887           0 :         while (lpos > 0) {
     888           0 :           uint32_t val = LittleEndian::readUint32(src);
     889           0 :           SetPixel(dst, mBitFields.mRed.Get8(val),
     890           0 :                         mBitFields.mGreen.Get8(val),
     891           0 :                         mBitFields.mBlue.Get8(val));
     892           0 :           --lpos;
     893           0 :           src += 4;
     894             :         }
     895             :       } else {
     896           0 :         bool anyHasAlpha = false;
     897           0 :         while (lpos > 0) {
     898           0 :           uint32_t val = LittleEndian::readUint32(src);
     899           0 :           SetPixel(dst, mBitFields.mRed.Get(val),
     900           0 :                         mBitFields.mGreen.Get(val),
     901           0 :                         mBitFields.mBlue.Get(val),
     902           0 :                         mBitFields.mAlpha.GetAlpha(val, anyHasAlpha));
     903           0 :           --lpos;
     904           0 :           src += 4;
     905             :         }
     906           0 :         if (anyHasAlpha) {
     907           0 :           MOZ_ASSERT(mMayHaveTransparency);
     908           0 :           mDoesHaveTransparency = true;
     909             :         }
     910             :       }
     911           0 :       break;
     912             : 
     913             :     default:
     914           0 :       MOZ_CRASH("Unsupported color depth; earlier check didn't catch it?");
     915             :   }
     916             : 
     917           0 :   FinishRow();
     918           0 :   return mCurrentRow == 0
     919             :        ? Transition::TerminateSuccess()
     920           0 :        : Transition::To(State::PIXEL_ROW, mPixelRowSize);
     921             : }
     922             : 
     923             : LexerTransition<nsBMPDecoder::State>
     924           0 : nsBMPDecoder::ReadRLESegment(const char* aData)
     925             : {
     926           0 :   if (mCurrentRow == 0) {
     927           0 :     return Transition::TerminateSuccess();
     928             :   }
     929             : 
     930           0 :   uint8_t byte1 = uint8_t(aData[0]);
     931           0 :   uint8_t byte2 = uint8_t(aData[1]);
     932             : 
     933           0 :   if (byte1 != RLE::ESCAPE) {
     934             :     // Encoded mode consists of two bytes: byte1 specifies the number of
     935             :     // consecutive pixels to be drawn using the color index contained in
     936             :     // byte2.
     937             :     //
     938             :     // Work around bitmaps that specify too many pixels.
     939             :     uint32_t pixelsNeeded =
     940           0 :       std::min<uint32_t>(mH.mWidth - mCurrentPos, byte1);
     941           0 :     if (pixelsNeeded) {
     942           0 :       uint32_t* dst = RowBuffer();
     943           0 :       mCurrentPos += pixelsNeeded;
     944           0 :       if (mH.mCompression == Compression::RLE8) {
     945           0 :         do {
     946           0 :           SetPixel(dst, byte2, mColors);
     947           0 :           pixelsNeeded --;
     948           0 :         } while (pixelsNeeded);
     949             :       } else {
     950           0 :         do {
     951           0 :           Set4BitPixel(dst, byte2, pixelsNeeded, mColors);
     952           0 :         } while (pixelsNeeded);
     953             :       }
     954             :     }
     955           0 :     return Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
     956             :   }
     957             : 
     958           0 :   if (byte2 == RLE::ESCAPE_EOL) {
     959           0 :     mCurrentPos = 0;
     960           0 :     FinishRow();
     961           0 :     return mCurrentRow == 0
     962             :          ? Transition::TerminateSuccess()
     963           0 :          : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
     964             :   }
     965             : 
     966           0 :   if (byte2 == RLE::ESCAPE_EOF) {
     967           0 :     return Transition::TerminateSuccess();
     968             :   }
     969             : 
     970           0 :   if (byte2 == RLE::ESCAPE_DELTA) {
     971           0 :     return Transition::To(State::RLE_DELTA, RLE::DELTA_LENGTH);
     972             :   }
     973             : 
     974             :   // Absolute mode. |byte2| gives the number of pixels. The length depends on
     975             :   // whether it's 4-bit or 8-bit RLE. Also, the length must be even (and zero
     976             :   // padding is used to achieve this when necessary).
     977           0 :   MOZ_ASSERT(mAbsoluteModeNumPixels == 0);
     978           0 :   mAbsoluteModeNumPixels = byte2;
     979           0 :   uint32_t length = byte2;
     980           0 :   if (mH.mCompression == Compression::RLE4) {
     981           0 :     length = (length + 1) / 2;    // halve, rounding up
     982             :   }
     983           0 :   if (length & 1) {
     984           0 :     length++;
     985             :   }
     986           0 :   return Transition::To(State::RLE_ABSOLUTE, length);
     987             : }
     988             : 
     989             : LexerTransition<nsBMPDecoder::State>
     990           0 : nsBMPDecoder::ReadRLEDelta(const char* aData)
     991             : {
     992             :   // Delta encoding makes it possible to skip pixels making part of the image
     993             :   // transparent.
     994           0 :   MOZ_ASSERT(mMayHaveTransparency);
     995           0 :   mDoesHaveTransparency = true;
     996             : 
     997           0 :   if (mDownscaler) {
     998             :     // Clear the skipped pixels. (This clears to the end of the row,
     999             :     // which is perfect if there's a Y delta and harmless if not).
    1000           0 :     mDownscaler->ClearRestOfRow(/* aStartingAtCol = */ mCurrentPos);
    1001             :   }
    1002             : 
    1003             :   // Handle the XDelta.
    1004           0 :   mCurrentPos += uint8_t(aData[0]);
    1005           0 :   if (mCurrentPos > mH.mWidth) {
    1006           0 :     mCurrentPos = mH.mWidth;
    1007             :   }
    1008             : 
    1009             :   // Handle the Y Delta.
    1010           0 :   int32_t yDelta = std::min<int32_t>(uint8_t(aData[1]), mCurrentRow);
    1011           0 :   mCurrentRow -= yDelta;
    1012             : 
    1013           0 :   if (mDownscaler && yDelta > 0) {
    1014             :     // Commit the current row (the first of the skipped rows).
    1015           0 :     mDownscaler->CommitRow();
    1016             : 
    1017             :     // Clear and commit the remaining skipped rows.
    1018           0 :     for (int32_t line = 1; line < yDelta; line++) {
    1019           0 :       mDownscaler->ClearRow();
    1020           0 :       mDownscaler->CommitRow();
    1021             :     }
    1022             :   }
    1023             : 
    1024           0 :   return mCurrentRow == 0
    1025             :        ? Transition::TerminateSuccess()
    1026           0 :        : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
    1027             : }
    1028             : 
    1029             : LexerTransition<nsBMPDecoder::State>
    1030           0 : nsBMPDecoder::ReadRLEAbsolute(const char* aData, size_t aLength)
    1031             : {
    1032           0 :   uint32_t n = mAbsoluteModeNumPixels;
    1033           0 :   mAbsoluteModeNumPixels = 0;
    1034             : 
    1035           0 :   if (mCurrentPos + n > uint32_t(mH.mWidth)) {
    1036             :     // Bad data. Stop decoding; at least part of the image may have been
    1037             :     // decoded.
    1038           0 :     return Transition::TerminateSuccess();
    1039             :   }
    1040             : 
    1041             :   // In absolute mode, n represents the number of pixels that follow, each of
    1042             :   // which contains the color index of a single pixel.
    1043           0 :   uint32_t* dst = RowBuffer();
    1044           0 :   uint32_t iSrc = 0;
    1045           0 :   uint32_t* oldPos = dst;
    1046           0 :   if (mH.mCompression == Compression::RLE8) {
    1047           0 :     while (n > 0) {
    1048           0 :       SetPixel(dst, aData[iSrc], mColors);
    1049           0 :       n--;
    1050           0 :       iSrc++;
    1051             :     }
    1052             :   } else {
    1053           0 :     while (n > 0) {
    1054           0 :       Set4BitPixel(dst, aData[iSrc], n, mColors);
    1055           0 :       iSrc++;
    1056             :     }
    1057             :   }
    1058           0 :   mCurrentPos += dst - oldPos;
    1059             : 
    1060             :   // We should read all the data (unless the last byte is zero padding).
    1061           0 :   MOZ_ASSERT(iSrc == aLength - 1 || iSrc == aLength);
    1062             : 
    1063           0 :   return Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
    1064             : }
    1065             : 
    1066             : } // namespace image
    1067             : } // namespace mozilla

Generated by: LCOV version 1.13