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

          Line data    Source code
       1             : /* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /* This is a Cross-Platform ICO Decoder, which should work everywhere, including
       7             :  * Big-Endian machines like the PowerPC. */
       8             : 
       9             : #include "nsICODecoder.h"
      10             : 
      11             : #include <stdlib.h>
      12             : 
      13             : #include "mozilla/EndianUtils.h"
      14             : #include "mozilla/Move.h"
      15             : 
      16             : #include "RasterImage.h"
      17             : 
      18             : using namespace mozilla::gfx;
      19             : 
      20             : namespace mozilla {
      21             : namespace image {
      22             : 
      23             : // Constants.
      24             : static const uint32_t ICOHEADERSIZE = 6;
      25             : static const uint32_t BITMAPINFOSIZE = bmp::InfoHeaderLength::WIN_ICO;
      26             : 
      27             : // ----------------------------------------
      28             : // Actual Data Processing
      29             : // ----------------------------------------
      30             : 
      31             : // Obtains the number of colors from the bits per pixel
      32             : uint16_t
      33           0 : nsICODecoder::GetNumColors()
      34             : {
      35           0 :   uint16_t numColors = 0;
      36           0 :   if (mBPP <= 8) {
      37           0 :     switch (mBPP) {
      38             :     case 1:
      39           0 :       numColors = 2;
      40           0 :       break;
      41             :     case 4:
      42           0 :       numColors = 16;
      43           0 :       break;
      44             :     case 8:
      45           0 :       numColors = 256;
      46           0 :       break;
      47             :     default:
      48           0 :       numColors = (uint16_t)-1;
      49             :     }
      50             :   }
      51           0 :   return numColors;
      52             : }
      53             : 
      54           0 : nsICODecoder::nsICODecoder(RasterImage* aImage)
      55             :   : Decoder(aImage)
      56           0 :   , mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE),
      57             :            Transition::TerminateSuccess())
      58             :   , mBiggestResourceColorDepth(0)
      59             :   , mBestResourceDelta(INT_MIN)
      60             :   , mBestResourceColorDepth(0)
      61             :   , mNumIcons(0)
      62             :   , mCurrIcon(0)
      63             :   , mBPP(0)
      64             :   , mMaskRowSize(0)
      65             :   , mCurrMaskLine(0)
      66             :   , mIsCursor(false)
      67           0 :   , mHasMaskAlpha(false)
      68           0 : { }
      69             : 
      70             : nsresult
      71           0 : nsICODecoder::FinishInternal()
      72             : {
      73             :   // We shouldn't be called in error cases
      74           0 :   MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
      75             : 
      76           0 :   return GetFinalStateFromContainedDecoder();
      77             : }
      78             : 
      79             : nsresult
      80           0 : nsICODecoder::FinishWithErrorInternal()
      81             : {
      82             :   // No need to assert !mInFrame here because this condition is enforced by
      83             :   // mContainedDecoder.
      84           0 :   return GetFinalStateFromContainedDecoder();
      85             : }
      86             : 
      87             : nsresult
      88           0 : nsICODecoder::GetFinalStateFromContainedDecoder()
      89             : {
      90           0 :   if (!mContainedDecoder) {
      91           0 :     return NS_OK;
      92             :   }
      93             : 
      94           0 :   MOZ_ASSERT(mContainedSourceBuffer,
      95             :              "Should have a SourceBuffer if we have a decoder");
      96             : 
      97             :   // Let the contained decoder finish up if necessary.
      98           0 :   if (!mContainedSourceBuffer->IsComplete()) {
      99           0 :     mContainedSourceBuffer->Complete(NS_OK);
     100           0 :     mContainedDecoder->Decode();
     101             :   }
     102             : 
     103             :   // Make our state the same as the state of the contained decoder.
     104           0 :   mDecodeDone = mContainedDecoder->GetDecodeDone();
     105           0 :   mProgress |= mContainedDecoder->TakeProgress();
     106           0 :   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
     107           0 :   mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
     108             : 
     109             :   // Propagate errors.
     110           0 :   nsresult rv = HasError() || mContainedDecoder->HasError()
     111           0 :               ? NS_ERROR_FAILURE
     112           0 :               : NS_OK;
     113             : 
     114           0 :   MOZ_ASSERT(NS_FAILED(rv) || !mCurrentFrame || mCurrentFrame->IsFinished());
     115           0 :   return rv;
     116             : }
     117             : 
     118             : bool
     119           0 : nsICODecoder::CheckAndFixBitmapSize(int8_t* aBIH)
     120             : {
     121             :   // Get the width from the BMP file information header. This is
     122             :   // (unintuitively) a signed integer; see the documentation at:
     123             :   //
     124             :   //   https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
     125             :   //
     126             :   // However, we reject negative widths since they aren't meaningful.
     127           0 :   const int32_t width = LittleEndian::readInt32(aBIH + 4);
     128           0 :   if (width <= 0 || width > 256) {
     129           0 :     return false;
     130             :   }
     131             : 
     132             :   // Verify that the BMP width matches the width we got from the ICO directory
     133             :   // entry. If not, decoding fails, because if we were to allow it to continue
     134             :   // the intrinsic size of the image wouldn't match the size of the decoded
     135             :   // surface.
     136           0 :   if (width != int32_t(GetRealWidth())) {
     137           0 :     return false;
     138             :   }
     139             : 
     140             :   // Get the height from the BMP file information header. This is also signed,
     141             :   // but in this case negative values are meaningful; see below.
     142           0 :   int32_t height = LittleEndian::readInt32(aBIH + 8);
     143           0 :   if (height == 0) {
     144           0 :     return false;
     145             :   }
     146             : 
     147             :   // BMPs can be stored inverted by having a negative height.
     148             :   // XXX(seth): Should we really be writing the absolute value into the BIH
     149             :   // below? Seems like this could be problematic for inverted BMPs.
     150           0 :   height = abs(height);
     151             : 
     152             :   // The height field is double the actual height of the image to account for
     153             :   // the AND mask. This is true even if the AND mask is not present.
     154           0 :   height /= 2;
     155           0 :   if (height > 256) {
     156           0 :     return false;
     157             :   }
     158             : 
     159             :   // Verify that the BMP height matches the height we got from the ICO directory
     160             :   // entry. If not, again, decoding fails.
     161           0 :   if (height != int32_t(GetRealHeight())) {
     162           0 :     return false;
     163             :   }
     164             : 
     165             :   // Fix the BMP height in the BIH so that the BMP decoder, which does not know
     166             :   // about the AND mask that may follow the actual bitmap, can work properly.
     167           0 :   LittleEndian::writeInt32(aBIH + 8, GetRealHeight());
     168             : 
     169           0 :   return true;
     170             : }
     171             : 
     172             : LexerTransition<ICOState>
     173           0 : nsICODecoder::ReadHeader(const char* aData)
     174             : {
     175             :   // If the third byte is 1, this is an icon. If 2, a cursor.
     176           0 :   if ((aData[2] != 1) && (aData[2] != 2)) {
     177           0 :     return Transition::TerminateFailure();
     178             :   }
     179           0 :   mIsCursor = (aData[2] == 2);
     180             : 
     181             :   // The fifth and sixth bytes specify the number of resources in the file.
     182           0 :   mNumIcons = LittleEndian::readUint16(aData + 4);
     183           0 :   if (mNumIcons == 0) {
     184           0 :     return Transition::TerminateSuccess(); // Nothing to do.
     185             :   }
     186             : 
     187             :   // Downscale-during-decode can end up decoding different resources in the ICO
     188             :   // file depending on the target size. Since the resources are not necessarily
     189             :   // scaled versions of the same image, some may be transparent and some may not
     190             :   // be. We could be precise about transparency if we decoded the metadata of
     191             :   // every resource, but for now we don't and it's safest to assume that
     192             :   // transparency could be present.
     193           0 :   PostHasTransparency();
     194             : 
     195           0 :   return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
     196             : }
     197             : 
     198             : size_t
     199           0 : nsICODecoder::FirstResourceOffset() const
     200             : {
     201           0 :   MOZ_ASSERT(mNumIcons > 0,
     202             :              "Calling FirstResourceOffset before processing header");
     203             : 
     204             :   // The first resource starts right after the directory, which starts right
     205             :   // after the ICO header.
     206           0 :   return ICOHEADERSIZE + mNumIcons * ICODIRENTRYSIZE;
     207             : }
     208             : 
     209             : LexerTransition<ICOState>
     210           0 : nsICODecoder::ReadDirEntry(const char* aData)
     211             : {
     212           0 :   mCurrIcon++;
     213             : 
     214             :   // Read the directory entry.
     215             :   IconDirEntry e;
     216           0 :   e.mWidth       = aData[0];
     217           0 :   e.mHeight      = aData[1];
     218           0 :   e.mColorCount  = aData[2];
     219           0 :   e.mReserved    = aData[3];
     220           0 :   e.mPlanes      = LittleEndian::readUint16(aData + 4);
     221           0 :   e.mBitCount    = LittleEndian::readUint16(aData + 6);
     222           0 :   e.mBytesInRes  = LittleEndian::readUint32(aData + 8);
     223           0 :   e.mImageOffset = LittleEndian::readUint32(aData + 12);
     224             : 
     225             :   // If an explicit output size was specified, we'll try to select the resource
     226             :   // that matches it best below.
     227           0 :   const Maybe<IntSize> desiredSize = ExplicitOutputSize();
     228             : 
     229             :   // Determine if this is the biggest resource we've seen so far. We always use
     230             :   // the biggest resource for the intrinsic size, and if we don't have a
     231             :   // specific desired size, we select it as the best resource as well.
     232           0 :   IntSize entrySize(GetRealWidth(e), GetRealHeight(e));
     233           0 :   if (e.mBitCount >= mBiggestResourceColorDepth &&
     234           0 :       entrySize.width * entrySize.height >=
     235           0 :         mBiggestResourceSize.width * mBiggestResourceSize.height) {
     236           0 :     mBiggestResourceSize = entrySize;
     237           0 :     mBiggestResourceColorDepth = e.mBitCount;
     238           0 :     mBiggestResourceHotSpot = IntSize(e.mXHotspot, e.mYHotspot);
     239             : 
     240           0 :     if (!desiredSize) {
     241           0 :       mDirEntry = e;
     242             :     }
     243             :   }
     244             : 
     245           0 :   mImageMetadata.AddNativeSize(entrySize);
     246             : 
     247           0 :   if (desiredSize) {
     248             :     // Calculate the delta between this resource's size and the desired size, so
     249             :     // we can see if it is better than our current-best option.  In the case of
     250             :     // several equally-good resources, we use the last one. "Better" in this
     251             :     // case is determined by |delta|, a measure of the difference in size
     252             :     // between the entry we've found and the desired size. We will choose the
     253             :     // smallest resource that is greater than or equal to the desired size (i.e.
     254             :     // we assume it's better to downscale a larger icon than to upscale a
     255             :     // smaller one).
     256           0 :     int32_t delta = std::min(entrySize.width - desiredSize->width,
     257           0 :                              entrySize.height - desiredSize->height);
     258           0 :     if (e.mBitCount >= mBestResourceColorDepth &&
     259           0 :         ((mBestResourceDelta < 0 && delta >= mBestResourceDelta) ||
     260           0 :          (delta >= 0 && delta <= mBestResourceDelta))) {
     261           0 :       mBestResourceDelta = delta;
     262           0 :       mBestResourceColorDepth = e.mBitCount;
     263           0 :       mDirEntry = e;
     264             :     }
     265             :   }
     266             : 
     267           0 :   if (mCurrIcon == mNumIcons) {
     268             :     // Ensure the resource we selected has an offset past the ICO headers.
     269           0 :     if (mDirEntry.mImageOffset < FirstResourceOffset()) {
     270           0 :       return Transition::TerminateFailure();
     271             :     }
     272             : 
     273             :     // If this is a cursor, set the hotspot. We use the hotspot from the biggest
     274             :     // resource since we also use that resource for the intrinsic size.
     275           0 :     if (mIsCursor) {
     276           0 :       mImageMetadata.SetHotspot(mBiggestResourceHotSpot.width,
     277           0 :                                 mBiggestResourceHotSpot.height);
     278             :     }
     279             : 
     280             :     // We always report the biggest resource's size as the intrinsic size; this
     281             :     // is necessary for downscale-during-decode to work since we won't even
     282             :     // attempt to *upscale* while decoding.
     283           0 :     PostSize(mBiggestResourceSize.width, mBiggestResourceSize.height);
     284           0 :     if (IsMetadataDecode()) {
     285           0 :       return Transition::TerminateSuccess();
     286             :     }
     287             : 
     288             :     // If the resource we selected matches the output size perfectly, we don't
     289             :     // need to do any downscaling.
     290           0 :     if (GetRealSize() == OutputSize()) {
     291           0 :       MOZ_ASSERT_IF(desiredSize, GetRealSize() == *desiredSize);
     292           0 :       MOZ_ASSERT_IF(!desiredSize, GetRealSize() == Size());
     293           0 :       mDownscaler.reset();
     294             :     }
     295             : 
     296           0 :     size_t offsetToResource = mDirEntry.mImageOffset - FirstResourceOffset();
     297             :     return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
     298             :                                     ICOState::SKIP_TO_RESOURCE,
     299           0 :                                     offsetToResource);
     300             :   }
     301             : 
     302           0 :   return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
     303             : }
     304             : 
     305             : LexerTransition<ICOState>
     306           0 : nsICODecoder::SniffResource(const char* aData)
     307             : {
     308             :   // We use the first PNGSIGNATURESIZE bytes to determine whether this resource
     309             :   // is a PNG or a BMP.
     310           0 :   bool isPNG = !memcmp(aData, nsPNGDecoder::pngSignatureBytes,
     311           0 :                        PNGSIGNATURESIZE);
     312           0 :   if (isPNG) {
     313             :     // Create a PNG decoder which will do the rest of the work for us.
     314           0 :     mContainedSourceBuffer = new SourceBuffer();
     315           0 :     mContainedSourceBuffer->ExpectLength(mDirEntry.mBytesInRes);
     316             :     mContainedDecoder =
     317           0 :       DecoderFactory::CreateDecoderForICOResource(DecoderType::PNG,
     318           0 :                                                   WrapNotNull(mContainedSourceBuffer),
     319           0 :                                                   WrapNotNull(this));
     320             : 
     321           0 :     if (!WriteToContainedDecoder(aData, PNGSIGNATURESIZE)) {
     322           0 :       return Transition::TerminateFailure();
     323             :     }
     324             : 
     325           0 :     if (mDirEntry.mBytesInRes <= PNGSIGNATURESIZE) {
     326           0 :       return Transition::TerminateFailure();
     327             :     }
     328             : 
     329             :     // Read in the rest of the PNG unbuffered.
     330           0 :     size_t toRead = mDirEntry.mBytesInRes - PNGSIGNATURESIZE;
     331             :     return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
     332             :                                     ICOState::READ_PNG,
     333           0 :                                     toRead);
     334             :   } else {
     335             :     // Make sure we have a sane size for the bitmap information header.
     336           0 :     int32_t bihSize = LittleEndian::readUint32(aData);
     337           0 :     if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
     338           0 :       return Transition::TerminateFailure();
     339             :     }
     340             : 
     341             :     // Buffer the first part of the bitmap information header.
     342           0 :     memcpy(mBIHraw, aData, PNGSIGNATURESIZE);
     343             : 
     344             :     // Read in the rest of the bitmap information header.
     345             :     return Transition::To(ICOState::READ_BIH,
     346           0 :                           BITMAPINFOSIZE - PNGSIGNATURESIZE);
     347             :   }
     348             : }
     349             : 
     350             : LexerTransition<ICOState>
     351           0 : nsICODecoder::ReadPNG(const char* aData, uint32_t aLen)
     352             : {
     353           0 :   if (!WriteToContainedDecoder(aData, aLen)) {
     354           0 :     return Transition::TerminateFailure();
     355             :   }
     356             : 
     357             :   // Raymond Chen says that 32bpp only are valid PNG ICOs
     358             :   // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
     359           0 :   if (!static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
     360           0 :     return Transition::TerminateFailure();
     361             :   }
     362             : 
     363           0 :   return Transition::ContinueUnbuffered(ICOState::READ_PNG);
     364             : }
     365             : 
     366             : LexerTransition<ICOState>
     367           0 : nsICODecoder::ReadBIH(const char* aData)
     368             : {
     369             :   // Buffer the rest of the bitmap information header.
     370           0 :   memcpy(mBIHraw + PNGSIGNATURESIZE, aData, BITMAPINFOSIZE - PNGSIGNATURESIZE);
     371             : 
     372             :   // Extract the BPP from the BIH header; it should be trusted over the one
     373             :   // we have from the ICO header which is usually set to 0.
     374           0 :   mBPP = LittleEndian::readUint16(mBIHraw + 14);
     375             : 
     376             :   // The ICO format when containing a BMP does not include the 14 byte
     377             :   // bitmap file header. So we create the BMP decoder via the constructor that
     378             :   // tells it to skip this, and pass in the required data (dataOffset) that
     379             :   // would have been present in the header.
     380           0 :   uint32_t dataOffset = bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE;
     381           0 :   if (mBPP <= 8) {
     382             :     // The color table is present only if BPP is <= 8.
     383           0 :     uint16_t numColors = GetNumColors();
     384           0 :     if (numColors == (uint16_t)-1) {
     385           0 :       return Transition::TerminateFailure();
     386             :     }
     387           0 :     dataOffset += 4 * numColors;
     388             :   }
     389             : 
     390             :   // Create a BMP decoder which will do most of the work for us; the exception
     391             :   // is the AND mask, which isn't present in standalone BMPs.
     392           0 :   mContainedSourceBuffer = new SourceBuffer();
     393           0 :   mContainedSourceBuffer->ExpectLength(mDirEntry.mBytesInRes);
     394             :   mContainedDecoder =
     395           0 :     DecoderFactory::CreateDecoderForICOResource(DecoderType::BMP,
     396           0 :                                                 WrapNotNull(mContainedSourceBuffer),
     397             :                                                 WrapNotNull(this),
     398           0 :                                                 Some(dataOffset));
     399             :   RefPtr<nsBMPDecoder> bmpDecoder =
     400           0 :     static_cast<nsBMPDecoder*>(mContainedDecoder.get());
     401             : 
     402             :   // Verify that the BIH width and height values match the ICO directory entry,
     403             :   // and fix the BIH height value to compensate for the fact that the underlying
     404             :   // BMP decoder doesn't know about AND masks.
     405           0 :   if (!CheckAndFixBitmapSize(reinterpret_cast<int8_t*>(mBIHraw))) {
     406           0 :     return Transition::TerminateFailure();
     407             :   }
     408             : 
     409             :   // Write out the BMP's bitmap info header.
     410           0 :   if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw))) {
     411           0 :     return Transition::TerminateFailure();
     412             :   }
     413             : 
     414             :   // Check to make sure we have valid color settings.
     415           0 :   uint16_t numColors = GetNumColors();
     416           0 :   if (numColors == uint16_t(-1)) {
     417           0 :     return Transition::TerminateFailure();
     418             :   }
     419             : 
     420             :   // Do we have an AND mask on this BMP? If so, we need to read it after we read
     421             :   // the BMP data itself.
     422           0 :   uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
     423           0 :   bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry.mBytesInRes;
     424           0 :   ICOState afterBMPState = hasANDMask ? ICOState::PREPARE_FOR_MASK
     425           0 :                                       : ICOState::FINISHED_RESOURCE;
     426             : 
     427             :   // Read in the rest of the BMP unbuffered.
     428             :   return Transition::ToUnbuffered(afterBMPState,
     429             :                                   ICOState::READ_BMP,
     430           0 :                                   bmpDataLength);
     431             : }
     432             : 
     433             : LexerTransition<ICOState>
     434           0 : nsICODecoder::ReadBMP(const char* aData, uint32_t aLen)
     435             : {
     436           0 :   if (!WriteToContainedDecoder(aData, aLen)) {
     437           0 :     return Transition::TerminateFailure();
     438             :   }
     439             : 
     440           0 :   return Transition::ContinueUnbuffered(ICOState::READ_BMP);
     441             : }
     442             : 
     443             : LexerTransition<ICOState>
     444           0 : nsICODecoder::PrepareForMask()
     445             : {
     446             :   RefPtr<nsBMPDecoder> bmpDecoder =
     447           0 :     static_cast<nsBMPDecoder*>(mContainedDecoder.get());
     448             : 
     449           0 :   uint16_t numColors = GetNumColors();
     450           0 :   MOZ_ASSERT(numColors != uint16_t(-1));
     451             : 
     452             :   // Determine the length of the AND mask.
     453             :   uint32_t bmpLengthWithHeader =
     454           0 :     BITMAPINFOSIZE + bmpDecoder->GetCompressedImageSize() + 4 * numColors;
     455           0 :   MOZ_ASSERT(bmpLengthWithHeader < mDirEntry.mBytesInRes);
     456           0 :   uint32_t maskLength = mDirEntry.mBytesInRes - bmpLengthWithHeader;
     457             : 
     458             :   // If the BMP provides its own transparency, we ignore the AND mask. We can
     459             :   // also obviously ignore it if the image has zero width or zero height.
     460           0 :   if (bmpDecoder->HasTransparency() ||
     461           0 :       GetRealWidth() == 0 || GetRealHeight() == 0) {
     462             :     return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
     463             :                                     ICOState::SKIP_MASK,
     464           0 :                                     maskLength);
     465             :   }
     466             : 
     467             :   // Compute the row size for the mask.
     468           0 :   mMaskRowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
     469             : 
     470             :   // If the expected size of the AND mask is larger than its actual size, then
     471             :   // we must have a truncated (and therefore corrupt) AND mask.
     472           0 :   uint32_t expectedLength = mMaskRowSize * GetRealHeight();
     473           0 :   if (maskLength < expectedLength) {
     474           0 :     return Transition::TerminateFailure();
     475             :   }
     476             : 
     477             :   // If we're downscaling, the mask is the wrong size for the surface we've
     478             :   // produced, so we need to downscale the mask into a temporary buffer and then
     479             :   // combine the mask's alpha values with the color values from the image.
     480           0 :   if (mDownscaler) {
     481           0 :     MOZ_ASSERT(bmpDecoder->GetImageDataLength() ==
     482             :                  mDownscaler->TargetSize().width *
     483             :                  mDownscaler->TargetSize().height *
     484             :                  sizeof(uint32_t));
     485           0 :     mMaskBuffer = MakeUnique<uint8_t[]>(bmpDecoder->GetImageDataLength());
     486           0 :     nsresult rv = mDownscaler->BeginFrame(GetRealSize(), Nothing(),
     487             :                                           mMaskBuffer.get(),
     488             :                                           /* aHasAlpha = */ true,
     489           0 :                                           /* aFlipVertically = */ true);
     490           0 :     if (NS_FAILED(rv)) {
     491           0 :       return Transition::TerminateFailure();
     492             :     }
     493             :   }
     494             : 
     495           0 :   mCurrMaskLine = GetRealHeight();
     496           0 :   return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
     497             : }
     498             : 
     499             : 
     500             : LexerTransition<ICOState>
     501           0 : nsICODecoder::ReadMaskRow(const char* aData)
     502             : {
     503           0 :   mCurrMaskLine--;
     504             : 
     505           0 :   uint8_t sawTransparency = 0;
     506             : 
     507             :   // Get the mask row we're reading.
     508           0 :   const uint8_t* mask = reinterpret_cast<const uint8_t*>(aData);
     509           0 :   const uint8_t* maskRowEnd = mask + mMaskRowSize;
     510             : 
     511             :   // Get the corresponding row of the mask buffer (if we're downscaling) or the
     512             :   // decoded image data (if we're not).
     513           0 :   uint32_t* decoded = nullptr;
     514           0 :   if (mDownscaler) {
     515             :     // Initialize the row to all white and fully opaque.
     516           0 :     memset(mDownscaler->RowBuffer(), 0xFF, GetRealWidth() * sizeof(uint32_t));
     517             : 
     518           0 :     decoded = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
     519             :   } else {
     520             :     RefPtr<nsBMPDecoder> bmpDecoder =
     521           0 :       static_cast<nsBMPDecoder*>(mContainedDecoder.get());
     522           0 :     uint32_t* imageData = bmpDecoder->GetImageData();
     523           0 :     if (!imageData) {
     524           0 :       return Transition::TerminateFailure();
     525             :     }
     526             : 
     527           0 :     decoded = imageData + mCurrMaskLine * GetRealWidth();
     528             :   }
     529             : 
     530           0 :   MOZ_ASSERT(decoded);
     531           0 :   uint32_t* decodedRowEnd = decoded + GetRealWidth();
     532             : 
     533             :   // Iterate simultaneously through the AND mask and the image data.
     534           0 :   while (mask < maskRowEnd) {
     535           0 :     uint8_t idx = *mask++;
     536           0 :     sawTransparency |= idx;
     537           0 :     for (uint8_t bit = 0x80; bit && decoded < decodedRowEnd; bit >>= 1) {
     538             :       // Clear pixel completely for transparency.
     539           0 :       if (idx & bit) {
     540           0 :         *decoded = 0;
     541             :       }
     542           0 :       decoded++;
     543             :     }
     544             :   }
     545             : 
     546           0 :   if (mDownscaler) {
     547           0 :     mDownscaler->CommitRow();
     548             :   }
     549             : 
     550             :   // If any bits are set in sawTransparency, then we know at least one pixel was
     551             :   // transparent.
     552           0 :   if (sawTransparency) {
     553           0 :     mHasMaskAlpha = true;
     554             :   }
     555             : 
     556           0 :   if (mCurrMaskLine == 0) {
     557           0 :     return Transition::To(ICOState::FINISH_MASK, 0);
     558             :   }
     559             : 
     560           0 :   return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
     561             : }
     562             : 
     563             : LexerTransition<ICOState>
     564           0 : nsICODecoder::FinishMask()
     565             : {
     566             :   // If we're downscaling, we now have the appropriate alpha values in
     567             :   // mMaskBuffer. We just need to transfer them to the image.
     568           0 :   if (mDownscaler) {
     569             :     // Retrieve the image data.
     570             :     RefPtr<nsBMPDecoder> bmpDecoder =
     571           0 :       static_cast<nsBMPDecoder*>(mContainedDecoder.get());
     572           0 :     uint8_t* imageData = reinterpret_cast<uint8_t*>(bmpDecoder->GetImageData());
     573           0 :     if (!imageData) {
     574           0 :       return Transition::TerminateFailure();
     575             :     }
     576             : 
     577             :     // Iterate through the alpha values, copying from mask to image.
     578           0 :     MOZ_ASSERT(mMaskBuffer);
     579           0 :     MOZ_ASSERT(bmpDecoder->GetImageDataLength() > 0);
     580           0 :     for (size_t i = 3 ; i < bmpDecoder->GetImageDataLength() ; i += 4) {
     581           0 :       imageData[i] = mMaskBuffer[i];
     582             :     }
     583             :   }
     584             : 
     585           0 :   return Transition::To(ICOState::FINISHED_RESOURCE, 0);
     586             : }
     587             : 
     588             : LexerTransition<ICOState>
     589           0 : nsICODecoder::FinishResource()
     590             : {
     591             :   // Make sure the actual size of the resource matches the size in the directory
     592             :   // entry. If not, we consider the image corrupt.
     593           0 :   if (mContainedDecoder->HasSize() &&
     594           0 :       mContainedDecoder->Size() != GetRealSize()) {
     595           0 :     return Transition::TerminateFailure();
     596             :   }
     597             : 
     598             :   // Finalize the frame which we deferred to ensure we could modify the final
     599             :   // result (e.g. to apply the BMP mask).
     600           0 :   MOZ_ASSERT(!mContainedDecoder->GetFinalizeFrames());
     601           0 :   if (mCurrentFrame) {
     602           0 :     mCurrentFrame->FinalizeSurface();
     603             :   }
     604             : 
     605           0 :   return Transition::TerminateSuccess();
     606             : }
     607             : 
     608             : LexerResult
     609           0 : nsICODecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
     610             : {
     611           0 :   MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
     612             : 
     613             :   return mLexer.Lex(aIterator, aOnResume,
     614           0 :                     [=](ICOState aState, const char* aData, size_t aLength) {
     615           0 :     switch (aState) {
     616             :       case ICOState::HEADER:
     617           0 :         return ReadHeader(aData);
     618             :       case ICOState::DIR_ENTRY:
     619           0 :         return ReadDirEntry(aData);
     620             :       case ICOState::SKIP_TO_RESOURCE:
     621           0 :         return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
     622             :       case ICOState::FOUND_RESOURCE:
     623           0 :         return Transition::To(ICOState::SNIFF_RESOURCE, PNGSIGNATURESIZE);
     624             :       case ICOState::SNIFF_RESOURCE:
     625           0 :         return SniffResource(aData);
     626             :       case ICOState::READ_PNG:
     627           0 :         return ReadPNG(aData, aLength);
     628             :       case ICOState::READ_BIH:
     629           0 :         return ReadBIH(aData);
     630             :       case ICOState::READ_BMP:
     631           0 :         return ReadBMP(aData, aLength);
     632             :       case ICOState::PREPARE_FOR_MASK:
     633           0 :         return PrepareForMask();
     634             :       case ICOState::READ_MASK_ROW:
     635           0 :         return ReadMaskRow(aData);
     636             :       case ICOState::FINISH_MASK:
     637           0 :         return FinishMask();
     638             :       case ICOState::SKIP_MASK:
     639           0 :         return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
     640             :       case ICOState::FINISHED_RESOURCE:
     641           0 :         return FinishResource();
     642             :       default:
     643           0 :         MOZ_CRASH("Unknown ICOState");
     644             :     }
     645           0 :   });
     646             : }
     647             : 
     648             : bool
     649           0 : nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount)
     650             : {
     651           0 :   MOZ_ASSERT(mContainedDecoder);
     652           0 :   MOZ_ASSERT(mContainedSourceBuffer);
     653             : 
     654             :   // Append the provided data to the SourceBuffer that the contained decoder is
     655             :   // reading from.
     656           0 :   mContainedSourceBuffer->Append(aBuffer, aCount);
     657             : 
     658           0 :   bool succeeded = true;
     659             : 
     660             :   // Write to the contained decoder. If we run out of data, the ICO decoder will
     661             :   // get resumed when there's more data available, as usual, so we don't need
     662             :   // the contained decoder to get resumed too. To avoid that, we provide an
     663             :   // IResumable which just does nothing.
     664           0 :   LexerResult result = mContainedDecoder->Decode();
     665           0 :   if (result == LexerResult(TerminalState::FAILURE)) {
     666           0 :     succeeded = false;
     667             :   }
     668             : 
     669           0 :   MOZ_ASSERT(result != LexerResult(Yield::OUTPUT_AVAILABLE),
     670             :              "Unexpected yield");
     671             : 
     672             :   // Make our state the same as the state of the contained decoder, and
     673             :   // propagate errors.
     674           0 :   mProgress |= mContainedDecoder->TakeProgress();
     675           0 :   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
     676           0 :   if (mContainedDecoder->HasError()) {
     677           0 :     succeeded = false;
     678             :   }
     679             : 
     680           0 :   return succeeded;
     681             : }
     682             : 
     683             : } // namespace image
     684             : } // namespace mozilla

Generated by: LCOV version 1.13