tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsICODecoder.cpp (25529B)


      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 <utility>
     12 
     13 #include "RasterImage.h"
     14 #include "mozilla/EndianUtils.h"
     15 #include "mozilla/gfx/Swizzle.h"
     16 #include "mozilla/UniquePtrExtensions.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 nsICODecoder::GetNumColors() {
     33  uint16_t numColors = 0;
     34  if (mBPP <= 8) {
     35    switch (mBPP) {
     36      case 1:
     37        numColors = 2;
     38        break;
     39      case 4:
     40        numColors = 16;
     41        break;
     42      case 8:
     43        numColors = 256;
     44        break;
     45      default:
     46        numColors = (uint16_t)-1;
     47    }
     48  }
     49  return numColors;
     50 }
     51 
     52 nsICODecoder::nsICODecoder(RasterImage* aImage)
     53    : Decoder(aImage),
     54      mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE),
     55             Transition::TerminateSuccess()),
     56      mDirEntry(nullptr),
     57      mNumIcons(0),
     58      mCurrIcon(0),
     59      mBPP(0),
     60      mMaskRowSize(0),
     61      mCurrMaskLine(0),
     62      mIsCursor(false),
     63      mHasMaskAlpha(false) {}
     64 
     65 nsresult nsICODecoder::FinishInternal() {
     66  // We shouldn't be called in error cases
     67  MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
     68 
     69  return GetFinalStateFromContainedDecoder();
     70 }
     71 
     72 nsresult nsICODecoder::FinishWithErrorInternal() {
     73  // No need to assert !mInFrame here because this condition is enforced by
     74  // mContainedDecoder.
     75  return GetFinalStateFromContainedDecoder();
     76 }
     77 
     78 nsresult nsICODecoder::GetFinalStateFromContainedDecoder() {
     79  if (!mContainedDecoder) {
     80    return NS_OK;
     81  }
     82 
     83  // Let the contained decoder finish up if necessary.
     84  FlushContainedDecoder();
     85 
     86  // Make our state the same as the state of the contained decoder.
     87  mDecodeDone = mContainedDecoder->GetDecodeDone();
     88  mProgress |= mContainedDecoder->TakeProgress();
     89  mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
     90  mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
     91 
     92  // Finalize the frame which we deferred to ensure we could modify the final
     93  // result (e.g. to apply the BMP mask).
     94  MOZ_ASSERT(!mContainedDecoder->GetFinalizeFrames());
     95  if (mCurrentFrame) {
     96    mCurrentFrame->FinalizeSurface();
     97  }
     98 
     99  // Propagate errors.
    100  nsresult rv =
    101      HasError() || mContainedDecoder->HasError() ? NS_ERROR_FAILURE : NS_OK;
    102 
    103  MOZ_ASSERT(NS_FAILED(rv) || !mCurrentFrame || mCurrentFrame->IsFinished());
    104  return rv;
    105 }
    106 
    107 LexerTransition<ICOState> nsICODecoder::ReadHeader(const char* aData) {
    108  // If the third byte is 1, this is an icon. If 2, a cursor.
    109  if ((aData[2] != 1) && (aData[2] != 2)) {
    110    return Transition::TerminateFailure();
    111  }
    112  mIsCursor = (aData[2] == 2);
    113 
    114  // The fifth and sixth bytes specify the number of resources in the file.
    115  mNumIcons = LittleEndian::readUint16(aData + 4);
    116  if (mNumIcons == 0) {
    117    return Transition::TerminateSuccess();  // Nothing to do.
    118  }
    119 
    120  // Downscale-during-decode can end up decoding different resources in the ICO
    121  // file depending on the target size. Since the resources are not necessarily
    122  // scaled versions of the same image, some may be transparent and some may not
    123  // be. We could be precise about transparency if we decoded the metadata of
    124  // every resource, but for now we don't and it's safest to assume that
    125  // transparency could be present.
    126  PostHasTransparency();
    127 
    128  return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
    129 }
    130 
    131 size_t nsICODecoder::FirstResourceOffset() const {
    132  MOZ_ASSERT(mNumIcons > 0,
    133             "Calling FirstResourceOffset before processing header");
    134 
    135  // The first resource starts right after the directory, which starts right
    136  // after the ICO header.
    137  return ICOHEADERSIZE + mNumIcons * ICODIRENTRYSIZE;
    138 }
    139 
    140 LexerTransition<ICOState> nsICODecoder::ReadDirEntry(const char* aData) {
    141  mCurrIcon++;
    142 
    143  // Ensure the resource has an offset past the ICO headers.
    144  uint32_t offset = LittleEndian::readUint32(aData + 12);
    145  if (offset >= FirstResourceOffset()) {
    146    // Read the directory entry.
    147    IconDirEntryEx e;
    148    e.mWidth = aData[0];
    149    e.mHeight = aData[1];
    150    e.mColorCount = aData[2];
    151    e.mReserved = aData[3];
    152    e.mPlanes = LittleEndian::readUint16(aData + 4);
    153    e.mBitCount = LittleEndian::readUint16(aData + 6);
    154    e.mBytesInRes = LittleEndian::readUint32(aData + 8);
    155    e.mImageOffset = offset;
    156    e.mSize = OrientedIntSize(e.mWidth, e.mHeight);
    157 
    158    // Only accept entries with sufficient resource data to actually contain
    159    // some image data.
    160    if (e.mBytesInRes > BITMAPINFOSIZE) {
    161      if (e.mWidth == 0 || e.mHeight == 0) {
    162        mUnsizedDirEntries.AppendElement(e);
    163      } else {
    164        mDirEntries.AppendElement(e);
    165      }
    166    }
    167  }
    168 
    169  if (mCurrIcon == mNumIcons) {
    170    if (mUnsizedDirEntries.IsEmpty()) {
    171      return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
    172    }
    173    return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
    174  }
    175 
    176  return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
    177 }
    178 
    179 LexerTransition<ICOState> nsICODecoder::IterateUnsizedDirEntry() {
    180  MOZ_ASSERT(!mUnsizedDirEntries.IsEmpty());
    181 
    182  if (!mDirEntry) {
    183    // The first time we are here, there is no entry selected. We must prepare a
    184    // new iterator for the contained decoder to advance as it wills. Cloning at
    185    // this point ensures it will begin at the end of the dir entries.
    186    mReturnIterator = mLexer.Clone(*mIterator, SIZE_MAX);
    187    if (mReturnIterator.isNothing()) {
    188      // If we cannot read further than this point, then there is no resource
    189      // data to read.
    190      return Transition::TerminateFailure();
    191    }
    192  } else {
    193    // We have already selected an entry which means a metadata decoder has
    194    // finished. Verify the size is valid and if so, add to the discovered
    195    // resources.
    196    if (mDirEntry->mSize.width > 0 && mDirEntry->mSize.height > 0) {
    197      mDirEntries.AppendElement(*mDirEntry);
    198    }
    199 
    200    // Remove the entry from the unsized list either way.
    201    mDirEntry = nullptr;
    202    mUnsizedDirEntries.RemoveElementAt(0);
    203 
    204    // Our iterator is at an unknown point, so reset it to the point that we
    205    // saved.
    206    mIterator = mLexer.Clone(*mReturnIterator, SIZE_MAX);
    207    if (mIterator.isNothing()) {
    208      MOZ_ASSERT_UNREACHABLE("Cannot re-clone return iterator");
    209      return Transition::TerminateFailure();
    210    }
    211  }
    212 
    213  // There are no more unsized entries, so we can finally decide which entry to
    214  // select for decoding.
    215  if (mUnsizedDirEntries.IsEmpty()) {
    216    mReturnIterator.reset();
    217    return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
    218  }
    219 
    220  // Move to the resource data to start metadata decoding.
    221  mDirEntry = &mUnsizedDirEntries[0];
    222  size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
    223  return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
    224                                  ICOState::SKIP_TO_RESOURCE, offsetToResource);
    225 }
    226 
    227 LexerTransition<ICOState> nsICODecoder::FinishDirEntry() {
    228  MOZ_ASSERT(!mDirEntry);
    229 
    230  if (mDirEntries.IsEmpty()) {
    231    return Transition::TerminateFailure();
    232  }
    233 
    234  // If an explicit output size was specified, we'll try to select the resource
    235  // that matches it best below.
    236  const Maybe<OrientedIntSize> desiredSize = ExplicitOutputSize();
    237 
    238  // Determine the biggest resource. We always use the biggest resource for the
    239  // intrinsic size, and if we don't have a specific desired size, we select it
    240  // as the best resource as well.
    241  int32_t bestDelta = INT32_MIN;
    242  IconDirEntryEx* biggestEntry = nullptr;
    243 
    244  for (size_t i = 0; i < mDirEntries.Length(); ++i) {
    245    IconDirEntryEx& e = mDirEntries[i];
    246    mImageMetadata.AddNativeSize(e.mSize);
    247 
    248    if (!biggestEntry ||
    249        (e.mBitCount >= biggestEntry->mBitCount &&
    250         e.mSize.width * e.mSize.height >=
    251             biggestEntry->mSize.width * biggestEntry->mSize.height)) {
    252      biggestEntry = &e;
    253 
    254      if (!desiredSize) {
    255        mDirEntry = &e;
    256      }
    257    }
    258 
    259    if (desiredSize) {
    260      // Calculate the delta between this resource's size and the desired size,
    261      // so we can see if it is better than our current-best option.  In the
    262      // case of several equally-good resources, we use the last one. "Better"
    263      // in this case is determined by |delta|, a measure of the difference in
    264      // size between the entry we've found and the desired size. We will choose
    265      // the smallest resource that is greater than or equal to the desired size
    266      // (i.e. we assume it's better to downscale a larger icon than to upscale
    267      // a smaller one).
    268      int32_t delta = std::min(e.mSize.width - desiredSize->width,
    269                               e.mSize.height - desiredSize->height);
    270      if (!mDirEntry || (e.mBitCount >= mDirEntry->mBitCount &&
    271                         ((bestDelta < 0 && delta >= bestDelta) ||
    272                          (delta >= 0 && delta <= bestDelta)))) {
    273        mDirEntry = &e;
    274        bestDelta = delta;
    275      }
    276    }
    277  }
    278 
    279  MOZ_ASSERT(mDirEntry);
    280  MOZ_ASSERT(biggestEntry);
    281 
    282  // If this is a cursor, set the hotspot. We use the hotspot from the biggest
    283  // resource since we also use that resource for the intrinsic size.
    284  if (mIsCursor) {
    285    mImageMetadata.SetHotspot(biggestEntry->mXHotspot, biggestEntry->mYHotspot);
    286  }
    287 
    288  // We always report the biggest resource's size as the intrinsic size; this
    289  // is necessary for downscale-during-decode to work since we won't even
    290  // attempt to *upscale* while decoding.
    291  PostSize(biggestEntry->mSize.width, biggestEntry->mSize.height);
    292  if (WantsFrameCount()) {
    293    PostFrameCount(/* aFrameCount */ 1);
    294  }
    295  if (HasError()) {
    296    return Transition::TerminateFailure();
    297  }
    298 
    299  if (IsMetadataDecode()) {
    300    return Transition::TerminateSuccess();
    301  }
    302 
    303  if (mDirEntry->mSize == OutputSize()) {
    304    // If the resource we selected matches the output size perfectly, we don't
    305    // need to do any downscaling.
    306    MOZ_ASSERT_IF(desiredSize, mDirEntry->mSize == *desiredSize);
    307    MOZ_ASSERT_IF(!desiredSize, mDirEntry->mSize == Size());
    308  } else if (OutputSize().width < mDirEntry->mSize.width ||
    309             OutputSize().height < mDirEntry->mSize.height) {
    310    // Create a downscaler if we need to downscale.
    311    //
    312    // TODO(aosmond): This is the last user of Downscaler. We should switch this
    313    // to SurfacePipe as well so we can remove the code from tree.
    314    mDownscaler.emplace(OutputSize().ToUnknownSize());
    315  }
    316 
    317  size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
    318  return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
    319                                  ICOState::SKIP_TO_RESOURCE, offsetToResource);
    320 }
    321 
    322 LexerTransition<ICOState> nsICODecoder::SniffResource(const char* aData) {
    323  MOZ_ASSERT(mDirEntry);
    324 
    325  // We have BITMAPINFOSIZE bytes buffered at this point. We know an embedded
    326  // BMP will have at least that many bytes by definition. We can also infer
    327  // that any valid embedded PNG will contain that many bytes as well because:
    328  //    BITMAPINFOSIZE
    329  //      <
    330  //    signature (8 bytes) +
    331  //    IHDR (12 bytes header + 13 bytes data)
    332  //    IDAT (12 bytes header)
    333 
    334  // We use the first PNGSIGNATURESIZE bytes to determine whether this resource
    335  // is a PNG or a BMP.
    336  bool isPNG =
    337      !memcmp(aData, nsPNGDecoder::pngSignatureBytes, PNGSIGNATURESIZE);
    338  if (isPNG) {
    339    if (mDirEntry->mBytesInRes <= BITMAPINFOSIZE) {
    340      return Transition::TerminateFailure();
    341    }
    342 
    343    // Prepare a new iterator for the contained decoder to advance as it wills.
    344    // Cloning at the point ensures it will begin at the resource offset.
    345    Maybe<SourceBufferIterator> containedIterator =
    346        mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
    347    if (containedIterator.isNothing()) {
    348      return Transition::TerminateFailure();
    349    }
    350 
    351    // Create a PNG decoder which will do the rest of the work for us.
    352    bool metadataDecode = mReturnIterator.isSome();
    353    Maybe<OrientedIntSize> expectedSize =
    354        metadataDecode ? Nothing() : Some(mDirEntry->mSize);
    355    mContainedDecoder = DecoderFactory::CreateDecoderForICOResource(
    356        DecoderType::PNG, std::move(containedIterator.ref()), WrapNotNull(this),
    357        metadataDecode, expectedSize);
    358 
    359    // Read in the rest of the PNG unbuffered.
    360    size_t toRead = mDirEntry->mBytesInRes - BITMAPINFOSIZE;
    361    return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
    362                                    ICOState::READ_RESOURCE, toRead);
    363  }
    364 
    365  // Make sure we have a sane size for the bitmap information header.
    366  int32_t bihSize = LittleEndian::readUint32(aData);
    367  if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
    368    return Transition::TerminateFailure();
    369  }
    370 
    371  // Read in the rest of the bitmap information header.
    372  return ReadBIH(aData);
    373 }
    374 
    375 LexerTransition<ICOState> nsICODecoder::ReadResource() {
    376  if (!FlushContainedDecoder()) {
    377    return Transition::TerminateFailure();
    378  }
    379 
    380  return Transition::ContinueUnbuffered(ICOState::READ_RESOURCE);
    381 }
    382 
    383 LexerTransition<ICOState> nsICODecoder::ReadBIH(const char* aData) {
    384  MOZ_ASSERT(mDirEntry);
    385 
    386  // Extract the BPP from the BIH header; it should be trusted over the one
    387  // we have from the ICO header which is usually set to 0.
    388  mBPP = LittleEndian::readUint16(aData + 14);
    389 
    390  // Check to make sure we have valid color settings.
    391  uint16_t numColors = GetNumColors();
    392  if (numColors == uint16_t(-1)) {
    393    return Transition::TerminateFailure();
    394  }
    395 
    396  // The color table is present only if BPP is <= 8.
    397  MOZ_ASSERT_IF(mBPP > 8, numColors == 0);
    398 
    399  // The ICO format when containing a BMP does not include the 14 byte
    400  // bitmap file header. So we create the BMP decoder via the constructor that
    401  // tells it to skip this, and pass in the required data (dataOffset) that
    402  // would have been present in the header.
    403  uint32_t dataOffset =
    404      bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE + 4 * numColors;
    405 
    406  // Prepare a new iterator for the contained decoder to advance as it wills.
    407  // Cloning at the point ensures it will begin at the resource offset.
    408  Maybe<SourceBufferIterator> containedIterator =
    409      mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
    410  if (containedIterator.isNothing()) {
    411    return Transition::TerminateFailure();
    412  }
    413 
    414  // Create a BMP decoder which will do most of the work for us; the exception
    415  // is the AND mask, which isn't present in standalone BMPs.
    416  bool metadataDecode = mReturnIterator.isSome();
    417  Maybe<OrientedIntSize> expectedSize =
    418      metadataDecode ? Nothing() : Some(mDirEntry->mSize);
    419  mContainedDecoder = DecoderFactory::CreateDecoderForICOResource(
    420      DecoderType::BMP, std::move(containedIterator.ref()), WrapNotNull(this),
    421      metadataDecode, expectedSize, Some(dataOffset));
    422 
    423  RefPtr<nsBMPDecoder> bmpDecoder =
    424      static_cast<nsBMPDecoder*>(mContainedDecoder.get());
    425 
    426  // Ensure the decoder has parsed at least the BMP's bitmap info header.
    427  if (!FlushContainedDecoder()) {
    428    return Transition::TerminateFailure();
    429  }
    430 
    431  // If this is a metadata decode, FinishResource will any necessary checks.
    432  if (mContainedDecoder->IsMetadataDecode()) {
    433    return Transition::To(ICOState::FINISHED_RESOURCE, 0);
    434  }
    435 
    436  // Do we have an AND mask on this BMP? If so, we need to read it after we read
    437  // the BMP data itself.
    438  uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
    439  bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry->mBytesInRes;
    440  ICOState afterBMPState =
    441      hasANDMask ? ICOState::PREPARE_FOR_MASK : ICOState::FINISHED_RESOURCE;
    442 
    443  // Read in the rest of the BMP unbuffered.
    444  return Transition::ToUnbuffered(afterBMPState, ICOState::READ_RESOURCE,
    445                                  bmpDataLength);
    446 }
    447 
    448 LexerTransition<ICOState> nsICODecoder::PrepareForMask() {
    449  MOZ_ASSERT(mDirEntry);
    450  MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
    451 
    452  // We have received all of the data required by the BMP decoder so flushing
    453  // here guarantees the decode has finished.
    454  if (!FlushContainedDecoder()) {
    455    return Transition::TerminateFailure();
    456  }
    457 
    458  MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
    459 
    460  RefPtr<nsBMPDecoder> bmpDecoder =
    461      static_cast<nsBMPDecoder*>(mContainedDecoder.get());
    462 
    463  uint16_t numColors = GetNumColors();
    464  MOZ_ASSERT(numColors != uint16_t(-1));
    465 
    466  // Determine the length of the AND mask.
    467  uint32_t bmpLengthWithHeader =
    468      BITMAPINFOSIZE + bmpDecoder->GetCompressedImageSize() + 4 * numColors;
    469  MOZ_ASSERT(bmpLengthWithHeader < mDirEntry->mBytesInRes);
    470  uint32_t maskLength = mDirEntry->mBytesInRes - bmpLengthWithHeader;
    471 
    472  // If the BMP provides its own transparency, we ignore the AND mask.
    473  if (bmpDecoder->HasTransparency()) {
    474    return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
    475                                    ICOState::SKIP_MASK, maskLength);
    476  }
    477 
    478  // Compute the row size for the mask.
    479  mMaskRowSize = ((mDirEntry->mSize.width + 31) / 32) * 4;  // + 31 to round up
    480 
    481  // If the expected size of the AND mask is larger than its actual size, then
    482  // we must have a truncated (and therefore corrupt) AND mask.
    483  uint32_t expectedLength = mMaskRowSize * mDirEntry->mSize.height;
    484  if (maskLength < expectedLength) {
    485    return Transition::TerminateFailure();
    486  }
    487 
    488  // If we're downscaling, the mask is the wrong size for the surface we've
    489  // produced, so we need to downscale the mask into a temporary buffer and then
    490  // combine the mask's alpha values with the color values from the image.
    491  if (mDownscaler) {
    492    MOZ_ASSERT(bmpDecoder->GetImageDataLength() ==
    493               mDownscaler->TargetSize().width *
    494                   mDownscaler->TargetSize().height * sizeof(uint32_t));
    495    mMaskBuffer =
    496        MakeUniqueFallible<uint8_t[]>(bmpDecoder->GetImageDataLength());
    497    if (NS_WARN_IF(!mMaskBuffer)) {
    498      return Transition::TerminateFailure();
    499    }
    500    nsresult rv = mDownscaler->BeginFrame(
    501        mDirEntry->mSize.ToUnknownSize(), Nothing(), mMaskBuffer.get(),
    502        /* aFormat = */ gfx::SurfaceFormat::B8G8R8A8,
    503        /* aFlipVertically = */ true);
    504    if (NS_FAILED(rv)) {
    505      return Transition::TerminateFailure();
    506    }
    507  }
    508 
    509  mCurrMaskLine = mDirEntry->mSize.height;
    510  return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
    511 }
    512 
    513 LexerTransition<ICOState> nsICODecoder::ReadMaskRow(const char* aData) {
    514  MOZ_ASSERT(mDirEntry);
    515 
    516  mCurrMaskLine--;
    517 
    518  uint8_t sawTransparency = 0;
    519 
    520  // Get the mask row we're reading.
    521  const uint8_t* mask = reinterpret_cast<const uint8_t*>(aData);
    522  const uint8_t* maskRowEnd = mask + mMaskRowSize;
    523 
    524  // Get the corresponding row of the mask buffer (if we're downscaling) or the
    525  // decoded image data (if we're not).
    526  uint32_t* decoded = nullptr;
    527  if (mDownscaler) {
    528    // Initialize the row to all white and fully opaque.
    529    memset(mDownscaler->RowBuffer(), 0xFF,
    530           mDirEntry->mSize.width * sizeof(uint32_t));
    531 
    532    decoded = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
    533  } else {
    534    RefPtr<nsBMPDecoder> bmpDecoder =
    535        static_cast<nsBMPDecoder*>(mContainedDecoder.get());
    536    uint32_t* imageData = bmpDecoder->GetImageData();
    537    if (!imageData) {
    538      return Transition::TerminateFailure();
    539    }
    540 
    541    decoded = imageData + mCurrMaskLine * mDirEntry->mSize.width;
    542  }
    543 
    544  MOZ_ASSERT(decoded);
    545  uint32_t* decodedRowEnd = decoded + mDirEntry->mSize.width;
    546 
    547  // Iterate simultaneously through the AND mask and the image data.
    548  while (mask < maskRowEnd) {
    549    uint8_t idx = *mask++;
    550    sawTransparency |= idx;
    551    for (uint8_t bit = 0x80; bit && decoded < decodedRowEnd; bit >>= 1) {
    552      // Clear pixel completely for transparency.
    553      if (idx & bit) {
    554        *decoded = 0;
    555      }
    556      decoded++;
    557    }
    558  }
    559 
    560  if (mDownscaler) {
    561    mDownscaler->CommitRow();
    562  }
    563 
    564  // If any bits are set in sawTransparency, then we know at least one pixel was
    565  // transparent.
    566  if (sawTransparency) {
    567    mHasMaskAlpha = true;
    568  }
    569 
    570  if (mCurrMaskLine == 0) {
    571    return Transition::To(ICOState::FINISH_MASK, 0);
    572  }
    573 
    574  return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
    575 }
    576 
    577 LexerTransition<ICOState> nsICODecoder::FinishMask() {
    578  // If we're downscaling, we now have the appropriate alpha values in
    579  // mMaskBuffer. We just need to transfer them to the image.
    580  if (mDownscaler) {
    581    // Retrieve the image data.
    582    RefPtr<nsBMPDecoder> bmpDecoder =
    583        static_cast<nsBMPDecoder*>(mContainedDecoder.get());
    584    uint8_t* imageData = reinterpret_cast<uint8_t*>(bmpDecoder->GetImageData());
    585    if (!imageData) {
    586      return Transition::TerminateFailure();
    587    }
    588 
    589    // Iterate through the alpha values, copying from mask to image.
    590    MOZ_ASSERT(mMaskBuffer);
    591    MOZ_ASSERT(bmpDecoder->GetImageDataLength() > 0);
    592    for (size_t i = 3; i < bmpDecoder->GetImageDataLength(); i += 4) {
    593      imageData[i] = mMaskBuffer[i];
    594    }
    595    int32_t stride = mDownscaler->TargetSize().width * sizeof(uint32_t);
    596    DebugOnly<bool> ret =
    597        // We know the format is OS_RGBA because we always assume bmp's inside
    598        // ico's are transparent.
    599        PremultiplyData(imageData, stride, SurfaceFormat::OS_RGBA, imageData,
    600                        stride, SurfaceFormat::OS_RGBA,
    601                        mDownscaler->TargetSize());
    602    MOZ_ASSERT(ret);
    603  }
    604 
    605  return Transition::To(ICOState::FINISHED_RESOURCE, 0);
    606 }
    607 
    608 LexerTransition<ICOState> nsICODecoder::FinishResource() {
    609  MOZ_ASSERT(mDirEntry);
    610 
    611  // We have received all of the data required by the PNG/BMP decoder so
    612  // flushing here guarantees the decode has finished.
    613  if (!FlushContainedDecoder()) {
    614    return Transition::TerminateFailure();
    615  }
    616 
    617  MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
    618 
    619  // If it is a metadata decode, all we were trying to get was the size
    620  // information missing from the dir entry.
    621  if (mContainedDecoder->IsMetadataDecode()) {
    622    if (mContainedDecoder->HasSize()) {
    623      mDirEntry->mSize = mContainedDecoder->Size();
    624    }
    625    return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
    626  }
    627 
    628  // Raymond Chen says that 32bpp only are valid PNG ICOs
    629  // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
    630  if (!mContainedDecoder->IsValidICOResource()) {
    631    return Transition::TerminateFailure();
    632  }
    633 
    634  // This size from the resource should match that from the dir entry.
    635  MOZ_ASSERT_IF(mContainedDecoder->HasSize(),
    636                mContainedDecoder->Size() == mDirEntry->mSize);
    637 
    638  return Transition::TerminateSuccess();
    639 }
    640 
    641 LexerResult nsICODecoder::DoDecode(SourceBufferIterator& aIterator,
    642                                   IResumable* aOnResume) {
    643  MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
    644 
    645  return mLexer.Lex(
    646      aIterator, aOnResume,
    647      [=](ICOState aState, const char* aData, size_t aLength) {
    648        switch (aState) {
    649          case ICOState::HEADER:
    650            return ReadHeader(aData);
    651          case ICOState::DIR_ENTRY:
    652            return ReadDirEntry(aData);
    653          case ICOState::FINISHED_DIR_ENTRY:
    654            return FinishDirEntry();
    655          case ICOState::ITERATE_UNSIZED_DIR_ENTRY:
    656            return IterateUnsizedDirEntry();
    657          case ICOState::SKIP_TO_RESOURCE:
    658            return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
    659          case ICOState::FOUND_RESOURCE:
    660            return Transition::To(ICOState::SNIFF_RESOURCE, BITMAPINFOSIZE);
    661          case ICOState::SNIFF_RESOURCE:
    662            return SniffResource(aData);
    663          case ICOState::READ_RESOURCE:
    664            return ReadResource();
    665          case ICOState::PREPARE_FOR_MASK:
    666            return PrepareForMask();
    667          case ICOState::READ_MASK_ROW:
    668            return ReadMaskRow(aData);
    669          case ICOState::FINISH_MASK:
    670            return FinishMask();
    671          case ICOState::SKIP_MASK:
    672            return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
    673          case ICOState::FINISHED_RESOURCE:
    674            return FinishResource();
    675          default:
    676            MOZ_CRASH("Unknown ICOState");
    677        }
    678      });
    679 }
    680 
    681 bool nsICODecoder::FlushContainedDecoder() {
    682  MOZ_ASSERT(mContainedDecoder);
    683 
    684  bool succeeded = true;
    685 
    686  // If we run out of data, the ICO decoder will get resumed when there's more
    687  // data available, as usual, so we don't need the contained decoder to get
    688  // resumed too. To avoid that, we provide an IResumable which just does
    689  // nothing. All the caller needs to do is flush when there is new data.
    690  LexerResult result = mContainedDecoder->Decode();
    691  if (result == LexerResult(TerminalState::FAILURE)) {
    692    succeeded = false;
    693  }
    694 
    695  MOZ_ASSERT(result != LexerResult(Yield::OUTPUT_AVAILABLE),
    696             "Unexpected yield");
    697 
    698  // Make our state the same as the state of the contained decoder, and
    699  // propagate errors.
    700  mProgress |= mContainedDecoder->TakeProgress();
    701  mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
    702  if (mContainedDecoder->HasError()) {
    703    succeeded = false;
    704  }
    705 
    706  return succeeded;
    707 }
    708 
    709 }  // namespace image
    710 }  // namespace mozilla