image/decoders/nsICODecoder.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/image/decoders/nsICODecoder.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,625 @@
     1.4 +/* vim:set tw=80 expandtab softtabstop=4 ts=4 sw=4: */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/* This is a Cross-Platform ICO Decoder, which should work everywhere, including
    1.10 + * Big-Endian machines like the PowerPC. */
    1.11 +
    1.12 +#include <stdlib.h>
    1.13 +
    1.14 +#include "mozilla/Endian.h"
    1.15 +#include "nsICODecoder.h"
    1.16 +
    1.17 +#include "RasterImage.h"
    1.18 +
    1.19 +namespace mozilla {
    1.20 +namespace image {
    1.21 +
    1.22 +#define ICONCOUNTOFFSET 4
    1.23 +#define DIRENTRYOFFSET 6
    1.24 +#define BITMAPINFOSIZE 40
    1.25 +#define PREFICONSIZE 16
    1.26 +
    1.27 +// ----------------------------------------
    1.28 +// Actual Data Processing
    1.29 +// ----------------------------------------
    1.30 +
    1.31 +uint32_t
    1.32 +nsICODecoder::CalcAlphaRowSize() 
    1.33 +{
    1.34 +  // Calculate rowsize in DWORD's and then return in # of bytes
    1.35 +  uint32_t rowSize = (GetRealWidth() + 31) / 32; // + 31 to round up
    1.36 +  return rowSize * 4; // Return rowSize in bytes
    1.37 +}
    1.38 +
    1.39 +// Obtains the number of colors from the bits per pixel
    1.40 +uint16_t
    1.41 +nsICODecoder::GetNumColors() 
    1.42 +{
    1.43 +  uint16_t numColors = 0;
    1.44 +  if (mBPP <= 8) {
    1.45 +    switch (mBPP) {
    1.46 +    case 1:
    1.47 +      numColors = 2;
    1.48 +      break;
    1.49 +    case 4:
    1.50 +      numColors = 16;
    1.51 +      break;
    1.52 +    case 8:
    1.53 +      numColors = 256;
    1.54 +      break;
    1.55 +    default:
    1.56 +      numColors = (uint16_t)-1;
    1.57 +    }
    1.58 +  }
    1.59 +  return numColors;
    1.60 +}
    1.61 +
    1.62 +
    1.63 +nsICODecoder::nsICODecoder(RasterImage &aImage)
    1.64 + : Decoder(aImage)
    1.65 +{
    1.66 +  mPos = mImageOffset = mCurrIcon = mNumIcons = mBPP = mRowBytes = 0;
    1.67 +  mIsPNG = false;
    1.68 +  mRow = nullptr;
    1.69 +  mOldLine = mCurLine = 1; // Otherwise decoder will never start
    1.70 +}
    1.71 +
    1.72 +nsICODecoder::~nsICODecoder()
    1.73 +{
    1.74 +  if (mRow) {
    1.75 +    moz_free(mRow);
    1.76 +  }
    1.77 +}
    1.78 +
    1.79 +void
    1.80 +nsICODecoder::FinishInternal()
    1.81 +{
    1.82 +  // We shouldn't be called in error cases
    1.83 +  NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call FinishInternal after error!");
    1.84 +
    1.85 +  // Finish the internally used decoder as well
    1.86 +  if (mContainedDecoder) {
    1.87 +    mContainedDecoder->FinishSharedDecoder();
    1.88 +    mDecodeDone = mContainedDecoder->GetDecodeDone();
    1.89 +  }
    1.90 +}
    1.91 +
    1.92 +// Returns a buffer filled with the bitmap file header in little endian:
    1.93 +// Signature 2 bytes 'BM'
    1.94 +// FileSize	 4 bytes File size in bytes
    1.95 +// reserved	 4 bytes unused (=0)
    1.96 +// DataOffset	 4 bytes File offset to Raster Data
    1.97 +// Returns true if successful
    1.98 +bool nsICODecoder::FillBitmapFileHeaderBuffer(int8_t *bfh) 
    1.99 +{
   1.100 +  memset(bfh, 0, 14);
   1.101 +  bfh[0] = 'B';
   1.102 +  bfh[1] = 'M';
   1.103 +  int32_t dataOffset = 0;
   1.104 +  int32_t fileSize = 0;
   1.105 +  dataOffset = BFH_LENGTH + BITMAPINFOSIZE;
   1.106 +
   1.107 +  // The color table is present only if BPP is <= 8
   1.108 +  if (mDirEntry.mBitCount <= 8) {
   1.109 +    uint16_t numColors = GetNumColors();
   1.110 +    if (numColors == (uint16_t)-1) {
   1.111 +      return false;
   1.112 +    }
   1.113 +    dataOffset += 4 * numColors;
   1.114 +    fileSize = dataOffset + GetRealWidth() * GetRealHeight();
   1.115 +  } else {
   1.116 +    fileSize = dataOffset + (mDirEntry.mBitCount * GetRealWidth() * 
   1.117 +                             GetRealHeight()) / 8;
   1.118 +  }
   1.119 +
   1.120 +  NativeEndian::swapToLittleEndianInPlace(&fileSize, 1);
   1.121 +  memcpy(bfh + 2, &fileSize, sizeof(fileSize));
   1.122 +  NativeEndian::swapToLittleEndianInPlace(&dataOffset, 1);
   1.123 +  memcpy(bfh + 10, &dataOffset, sizeof(dataOffset));
   1.124 +  return true;
   1.125 +}
   1.126 +
   1.127 +// A BMP inside of an ICO has *2 height because of the AND mask
   1.128 +// that follows the actual bitmap.  The BMP shouldn't know about
   1.129 +// this difference though.
   1.130 +bool
   1.131 +nsICODecoder::FixBitmapHeight(int8_t *bih) 
   1.132 +{
   1.133 +  // Get the height from the BMP file information header
   1.134 +  int32_t height;
   1.135 +  memcpy(&height, bih + 8, sizeof(height));
   1.136 +  NativeEndian::swapFromLittleEndianInPlace(&height, 1);
   1.137 +  // BMPs can be stored inverted by having a negative height
   1.138 +  height = abs(height);
   1.139 +
   1.140 +  // The bitmap height is by definition * 2 what it should be to account for
   1.141 +  // the 'AND mask'. It is * 2 even if the `AND mask` is not present.
   1.142 +  height /= 2;
   1.143 +
   1.144 +  if (height > 256) {
   1.145 +    return false;
   1.146 +  }
   1.147 +
   1.148 +  // We should always trust the height from the bitmap itself instead of 
   1.149 +  // the ICO height.  So fix the ICO height.
   1.150 +  if (height == 256) {
   1.151 +    mDirEntry.mHeight = 0;
   1.152 +  } else {
   1.153 +    mDirEntry.mHeight = (int8_t)height;
   1.154 +  }
   1.155 +
   1.156 +  // Fix the BMP height in the BIH so that the BMP decoder can work properly
   1.157 +  NativeEndian::swapToLittleEndianInPlace(&height, 1);
   1.158 +  memcpy(bih + 8, &height, sizeof(height));
   1.159 +  return true;
   1.160 +}
   1.161 +
   1.162 +// We should always trust the contained resource for the width
   1.163 +// information over our own information.
   1.164 +bool
   1.165 +nsICODecoder::FixBitmapWidth(int8_t *bih) 
   1.166 +{
   1.167 +  // Get the width from the BMP file information header
   1.168 +  int32_t width;
   1.169 +  memcpy(&width, bih + 4, sizeof(width));
   1.170 +  NativeEndian::swapFromLittleEndianInPlace(&width, 1);
   1.171 +  if (width > 256) {
   1.172 +    return false;
   1.173 +  }
   1.174 +
   1.175 +  // We should always trust the width  from the bitmap itself instead of 
   1.176 +  // the ICO width.
   1.177 +  if (width == 256) {
   1.178 +    mDirEntry.mWidth = 0;
   1.179 +  } else {
   1.180 +    mDirEntry.mWidth = (int8_t)width;
   1.181 +  }
   1.182 +  return true;
   1.183 +}
   1.184 +
   1.185 +// The BMP information header's bits per pixel should be trusted
   1.186 +// more than what we have.  Usually the ICO's BPP is set to 0
   1.187 +int32_t 
   1.188 +nsICODecoder::ExtractBPPFromBitmap(int8_t *bih)
   1.189 +{
   1.190 +  int32_t bitsPerPixel;
   1.191 +  memcpy(&bitsPerPixel, bih + 14, sizeof(bitsPerPixel));
   1.192 +  NativeEndian::swapFromLittleEndianInPlace(&bitsPerPixel, 1);
   1.193 +  return bitsPerPixel;
   1.194 +}
   1.195 +
   1.196 +int32_t 
   1.197 +nsICODecoder::ExtractBIHSizeFromBitmap(int8_t *bih)
   1.198 +{
   1.199 +  int32_t headerSize;
   1.200 +  memcpy(&headerSize, bih, sizeof(headerSize));
   1.201 +  NativeEndian::swapFromLittleEndianInPlace(&headerSize, 1);
   1.202 +  return headerSize;
   1.203 +}
   1.204 +
   1.205 +void
   1.206 +nsICODecoder::SetHotSpotIfCursor() {
   1.207 +  if (!mIsCursor) {
   1.208 +    return;
   1.209 +  }
   1.210 +
   1.211 +  mImageMetadata.SetHotspot(mDirEntry.mXHotspot, mDirEntry.mYHotspot);
   1.212 +}
   1.213 +
   1.214 +void
   1.215 +nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
   1.216 +{
   1.217 +  NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
   1.218 +
   1.219 +  if (!aCount) {
   1.220 +    if (mContainedDecoder) {
   1.221 +      WriteToContainedDecoder(aBuffer, aCount, aStrategy);
   1.222 +    }
   1.223 +    return;
   1.224 +  }
   1.225 +
   1.226 +  while (aCount && (mPos < ICONCOUNTOFFSET)) { // Skip to the # of icons.
   1.227 +    if (mPos == 2) { // if the third byte is 1: This is an icon, 2: a cursor
   1.228 +      if ((*aBuffer != 1) && (*aBuffer != 2)) {
   1.229 +        PostDataError();
   1.230 +        return;
   1.231 +      }
   1.232 +      mIsCursor = (*aBuffer == 2);
   1.233 +    }
   1.234 +    mPos++; aBuffer++; aCount--;
   1.235 +  }
   1.236 +
   1.237 +  if (mPos == ICONCOUNTOFFSET && aCount >= 2) {
   1.238 +    mNumIcons = LittleEndian::readUint16(reinterpret_cast<const uint16_t*>(aBuffer));
   1.239 +    aBuffer += 2;
   1.240 +    mPos += 2;
   1.241 +    aCount -= 2;
   1.242 +  }
   1.243 +
   1.244 +  if (mNumIcons == 0)
   1.245 +    return; // Nothing to do.
   1.246 +
   1.247 +  uint16_t colorDepth = 0;
   1.248 +  nsIntSize prefSize = mImage.GetRequestedResolution();
   1.249 +  if (prefSize.width == 0 && prefSize.height == 0) {
   1.250 +    prefSize.SizeTo(PREFICONSIZE, PREFICONSIZE);
   1.251 +  }
   1.252 +
   1.253 +  // A measure of the difference in size between the entry we've found
   1.254 +  // and the requested size. We will choose the smallest image that is
   1.255 +  // >= requested size (i.e. we assume it's better to downscale a larger
   1.256 +  // icon than to upscale a smaller one).
   1.257 +  int32_t diff = INT_MIN;
   1.258 +
   1.259 +  // Loop through each entry's dir entry
   1.260 +  while (mCurrIcon < mNumIcons) { 
   1.261 +    if (mPos >= DIRENTRYOFFSET + (mCurrIcon * sizeof(mDirEntryArray)) && 
   1.262 +        mPos < DIRENTRYOFFSET + ((mCurrIcon + 1) * sizeof(mDirEntryArray))) {
   1.263 +      uint32_t toCopy = sizeof(mDirEntryArray) - 
   1.264 +                        (mPos - DIRENTRYOFFSET - mCurrIcon * sizeof(mDirEntryArray));
   1.265 +      if (toCopy > aCount) {
   1.266 +        toCopy = aCount;
   1.267 +      }
   1.268 +      memcpy(mDirEntryArray + sizeof(mDirEntryArray) - toCopy, aBuffer, toCopy);
   1.269 +      mPos += toCopy;
   1.270 +      aCount -= toCopy;
   1.271 +      aBuffer += toCopy;
   1.272 +    }
   1.273 +    if (aCount == 0)
   1.274 +      return; // Need more data
   1.275 +
   1.276 +    IconDirEntry e;
   1.277 +    if (mPos == (DIRENTRYOFFSET + ICODIRENTRYSIZE) + 
   1.278 +                (mCurrIcon * sizeof(mDirEntryArray))) {
   1.279 +      mCurrIcon++;
   1.280 +      ProcessDirEntry(e);
   1.281 +      // We can't use GetRealWidth and GetRealHeight here because those operate
   1.282 +      // on mDirEntry, here we are going through each item in the directory.
   1.283 +      // Calculate the delta between this image's size and the desired size,
   1.284 +      // so we can see if it is better than our current-best option.
   1.285 +      // In the case of several equally-good images, we use the last one.
   1.286 +      int32_t delta = (e.mWidth == 0 ? 256 : e.mWidth) - prefSize.width +
   1.287 +                      (e.mHeight == 0 ? 256 : e.mHeight) - prefSize.height;
   1.288 +      if (e.mBitCount >= colorDepth &&
   1.289 +          ((diff < 0 && delta >= diff) || (delta >= 0 && delta <= diff))) {
   1.290 +        diff = delta;
   1.291 +        mImageOffset = e.mImageOffset;
   1.292 +
   1.293 +        // ensure mImageOffset is >= size of the direntry headers (bug #245631)
   1.294 +        uint32_t minImageOffset = DIRENTRYOFFSET + 
   1.295 +                                  mNumIcons * sizeof(mDirEntryArray);
   1.296 +        if (mImageOffset < minImageOffset) {
   1.297 +          PostDataError();
   1.298 +          return;
   1.299 +        }
   1.300 +
   1.301 +        colorDepth = e.mBitCount;
   1.302 +        memcpy(&mDirEntry, &e, sizeof(IconDirEntry));
   1.303 +      }
   1.304 +    }
   1.305 +  }
   1.306 +
   1.307 +  if (mPos < mImageOffset) {
   1.308 +    // Skip to (or at least towards) the desired image offset
   1.309 +    uint32_t toSkip = mImageOffset - mPos;
   1.310 +    if (toSkip > aCount)
   1.311 +      toSkip = aCount;
   1.312 +
   1.313 +    mPos    += toSkip;
   1.314 +    aBuffer += toSkip;
   1.315 +    aCount  -= toSkip;
   1.316 +  }
   1.317 +
   1.318 +  // If we are within the first PNGSIGNATURESIZE bytes of the image data,
   1.319 +  // then we have either a BMP or a PNG.  We use the first PNGSIGNATURESIZE
   1.320 +  // bytes to determine which one we have.
   1.321 +  if (mCurrIcon == mNumIcons && mPos >= mImageOffset && 
   1.322 +      mPos < mImageOffset + PNGSIGNATURESIZE)
   1.323 +  {
   1.324 +    uint32_t toCopy = PNGSIGNATURESIZE - (mPos - mImageOffset);
   1.325 +    if (toCopy > aCount) {
   1.326 +      toCopy = aCount;
   1.327 +    }
   1.328 +
   1.329 +    memcpy(mSignature + (mPos - mImageOffset), aBuffer, toCopy);
   1.330 +    mPos += toCopy;
   1.331 +    aCount -= toCopy;
   1.332 +    aBuffer += toCopy;
   1.333 +
   1.334 +    mIsPNG = !memcmp(mSignature, nsPNGDecoder::pngSignatureBytes, 
   1.335 +                     PNGSIGNATURESIZE);
   1.336 +    if (mIsPNG) {
   1.337 +      mContainedDecoder = new nsPNGDecoder(mImage);
   1.338 +      mContainedDecoder->SetObserver(mObserver);
   1.339 +      mContainedDecoder->SetSizeDecode(IsSizeDecode());
   1.340 +      mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength,
   1.341 +                                           mColormap, mColormapSize,
   1.342 +                                           mCurrentFrame);
   1.343 +      if (!WriteToContainedDecoder(mSignature, PNGSIGNATURESIZE, aStrategy)) {
   1.344 +        return;
   1.345 +      }
   1.346 +    }
   1.347 +  }
   1.348 +
   1.349 +  // If we have a PNG, let the PNG decoder do all of the rest of the work
   1.350 +  if (mIsPNG && mContainedDecoder && mPos >= mImageOffset + PNGSIGNATURESIZE) {
   1.351 +    if (!WriteToContainedDecoder(aBuffer, aCount, aStrategy)) {
   1.352 +      return;
   1.353 +    }
   1.354 +
   1.355 +    if (!HasSize() && mContainedDecoder->HasSize()) {
   1.356 +      PostSize(mContainedDecoder->GetImageMetadata().GetWidth(),
   1.357 +               mContainedDecoder->GetImageMetadata().GetHeight());
   1.358 +    }
   1.359 +
   1.360 +    mPos += aCount;
   1.361 +    aBuffer += aCount;
   1.362 +    aCount = 0;
   1.363 +
   1.364 +    // Raymond Chen says that 32bpp only are valid PNG ICOs
   1.365 +    // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
   1.366 +    if (!IsSizeDecode() &&
   1.367 +        !static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
   1.368 +      PostDataError();
   1.369 +    }
   1.370 +    return;
   1.371 +  }
   1.372 +
   1.373 +  // We've processed all of the icon dir entries and are within the 
   1.374 +  // bitmap info size
   1.375 +  if (!mIsPNG && mCurrIcon == mNumIcons && mPos >= mImageOffset && 
   1.376 +      mPos >= mImageOffset + PNGSIGNATURESIZE && 
   1.377 +      mPos < mImageOffset + BITMAPINFOSIZE) {
   1.378 +
   1.379 +    // As we were decoding, we did not know if we had a PNG signature or the
   1.380 +    // start of a bitmap information header.  At this point we know we had
   1.381 +    // a bitmap information header and not a PNG signature, so fill the bitmap
   1.382 +    // information header with the data it should already have.
   1.383 +    memcpy(mBIHraw, mSignature, PNGSIGNATURESIZE);
   1.384 +
   1.385 +    // We've found the icon.
   1.386 +    uint32_t toCopy = sizeof(mBIHraw) - (mPos - mImageOffset);
   1.387 +    if (toCopy > aCount)
   1.388 +      toCopy = aCount;
   1.389 +
   1.390 +    memcpy(mBIHraw + (mPos - mImageOffset), aBuffer, toCopy);
   1.391 +    mPos += toCopy;
   1.392 +    aCount -= toCopy;
   1.393 +    aBuffer += toCopy;
   1.394 +  }
   1.395 +
   1.396 +  // If we have a BMP inside the ICO and we have read the BIH header
   1.397 +  if (!mIsPNG && mPos == mImageOffset + BITMAPINFOSIZE) {
   1.398 +
   1.399 +    // Make sure we have a sane value for the bitmap information header
   1.400 +    int32_t bihSize = ExtractBIHSizeFromBitmap(reinterpret_cast<int8_t*>(mBIHraw));
   1.401 +    if (bihSize != BITMAPINFOSIZE) {
   1.402 +      PostDataError();
   1.403 +      return;
   1.404 +    }
   1.405 +    // We are extracting the BPP from the BIH header as it should be trusted 
   1.406 +    // over the one we have from the icon header
   1.407 +    mBPP = ExtractBPPFromBitmap(reinterpret_cast<int8_t*>(mBIHraw));
   1.408 +    
   1.409 +    // Init the bitmap decoder which will do most of the work for us
   1.410 +    // It will do everything except the AND mask which isn't present in bitmaps
   1.411 +    // bmpDecoder is for local scope ease, it will be freed by mContainedDecoder
   1.412 +    nsBMPDecoder *bmpDecoder = new nsBMPDecoder(mImage);
   1.413 +    mContainedDecoder = bmpDecoder;
   1.414 +    bmpDecoder->SetUseAlphaData(true);
   1.415 +    mContainedDecoder->SetObserver(mObserver);
   1.416 +    mContainedDecoder->SetSizeDecode(IsSizeDecode());
   1.417 +    mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength,
   1.418 +                                         mColormap, mColormapSize,
   1.419 +                                         mCurrentFrame);
   1.420 +
   1.421 +    // The ICO format when containing a BMP does not include the 14 byte
   1.422 +    // bitmap file header. To use the code of the BMP decoder we need to 
   1.423 +    // generate this header ourselves and feed it to the BMP decoder.
   1.424 +    int8_t bfhBuffer[BMPFILEHEADERSIZE];
   1.425 +    if (!FillBitmapFileHeaderBuffer(bfhBuffer)) {
   1.426 +      PostDataError();
   1.427 +      return;
   1.428 +    }
   1.429 +    if (!WriteToContainedDecoder((const char*)bfhBuffer, sizeof(bfhBuffer), aStrategy)) {
   1.430 +      return;
   1.431 +    }
   1.432 +
   1.433 +    // Setup the cursor hot spot if one is present
   1.434 +    SetHotSpotIfCursor();
   1.435 +
   1.436 +    // Fix the ICO height from the BIH.
   1.437 +    // Fix the height on the BIH to be /2 so our BMP decoder will understand.
   1.438 +    if (!FixBitmapHeight(reinterpret_cast<int8_t*>(mBIHraw))) {
   1.439 +      PostDataError();
   1.440 +      return;
   1.441 +    }
   1.442 +
   1.443 +    // Fix the ICO width from the BIH.
   1.444 +    if (!FixBitmapWidth(reinterpret_cast<int8_t*>(mBIHraw))) {
   1.445 +      PostDataError();
   1.446 +      return;
   1.447 +    }
   1.448 +
   1.449 +    // Write out the BMP's bitmap info header
   1.450 +    if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw), aStrategy)) {
   1.451 +      return;
   1.452 +    }
   1.453 +
   1.454 +    PostSize(mContainedDecoder->GetImageMetadata().GetWidth(),
   1.455 +             mContainedDecoder->GetImageMetadata().GetHeight());
   1.456 +
   1.457 +    // We have the size. If we're doing a size decode, we got what
   1.458 +    // we came for.
   1.459 +    if (IsSizeDecode())
   1.460 +      return;
   1.461 +
   1.462 +    // Sometimes the ICO BPP header field is not filled out
   1.463 +    // so we should trust the contained resource over our own
   1.464 +    // information.
   1.465 +    mBPP = bmpDecoder->GetBitsPerPixel();
   1.466 +
   1.467 +    // Check to make sure we have valid color settings
   1.468 +    uint16_t numColors = GetNumColors();
   1.469 +    if (numColors == (uint16_t)-1) {
   1.470 +      PostDataError();
   1.471 +      return;
   1.472 +    }
   1.473 +  }
   1.474 +
   1.475 +  // If we have a BMP
   1.476 +  if (!mIsPNG && mContainedDecoder && mPos >= mImageOffset + BITMAPINFOSIZE) {
   1.477 +    uint16_t numColors = GetNumColors();
   1.478 +    if (numColors == (uint16_t)-1) {
   1.479 +      PostDataError();
   1.480 +      return;
   1.481 +    }
   1.482 +    // Feed the actual image data (not including headers) into the BMP decoder
   1.483 +    uint32_t bmpDataOffset = mDirEntry.mImageOffset + BITMAPINFOSIZE;
   1.484 +    uint32_t bmpDataEnd = mDirEntry.mImageOffset + BITMAPINFOSIZE + 
   1.485 +                          static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetCompressedImageSize() +
   1.486 +                          4 * numColors;
   1.487 +
   1.488 +    // If we are feeding in the core image data, but we have not yet
   1.489 +    // reached the ICO's 'AND buffer mask'
   1.490 +    if (mPos >= bmpDataOffset && mPos < bmpDataEnd) {
   1.491 +
   1.492 +      // Figure out how much data the BMP decoder wants
   1.493 +      uint32_t toFeed = bmpDataEnd - mPos;
   1.494 +      if (toFeed > aCount) {
   1.495 +        toFeed = aCount;
   1.496 +      }
   1.497 +
   1.498 +      if (!WriteToContainedDecoder(aBuffer, toFeed, aStrategy)) {
   1.499 +        return;
   1.500 +      }
   1.501 +
   1.502 +      mPos += toFeed;
   1.503 +      aCount -= toFeed;
   1.504 +      aBuffer += toFeed;
   1.505 +    }
   1.506 +  
   1.507 +    // If the bitmap is fully processed, treat any left over data as the ICO's
   1.508 +    // 'AND buffer mask' which appears after the bitmap resource.
   1.509 +    if (!mIsPNG && mPos >= bmpDataEnd) {
   1.510 +      // There may be an optional AND bit mask after the data.  This is
   1.511 +      // only used if the alpha data is not already set. The alpha data 
   1.512 +      // is used for 32bpp bitmaps as per the comment in ICODecoder.h
   1.513 +      // The alpha mask should be checked in all other cases.
   1.514 +      if (static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetBitsPerPixel() != 32 || 
   1.515 +          !static_cast<nsBMPDecoder*>(mContainedDecoder.get())->HasAlphaData()) {
   1.516 +        uint32_t rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
   1.517 +        if (mPos == bmpDataEnd) {
   1.518 +          mPos++;
   1.519 +          mRowBytes = 0;
   1.520 +          mCurLine = GetRealHeight();
   1.521 +          mRow = (uint8_t*)moz_realloc(mRow, rowSize);
   1.522 +          if (!mRow) {
   1.523 +            PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
   1.524 +            return;
   1.525 +          }
   1.526 +        }
   1.527 +
   1.528 +        // Ensure memory has been allocated before decoding.
   1.529 +        NS_ABORT_IF_FALSE(mRow, "mRow is null");
   1.530 +        if (!mRow) {
   1.531 +          PostDataError();
   1.532 +          return;
   1.533 +        }
   1.534 +
   1.535 +        while (mCurLine > 0 && aCount > 0) {
   1.536 +          uint32_t toCopy = std::min(rowSize - mRowBytes, aCount);
   1.537 +          if (toCopy) {
   1.538 +            memcpy(mRow + mRowBytes, aBuffer, toCopy);
   1.539 +            aCount -= toCopy;
   1.540 +            aBuffer += toCopy;
   1.541 +            mRowBytes += toCopy;
   1.542 +          }
   1.543 +          if (rowSize == mRowBytes) {
   1.544 +            mCurLine--;
   1.545 +            mRowBytes = 0;
   1.546 +
   1.547 +            uint32_t* imageData = 
   1.548 +              static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetImageData();
   1.549 +            if (!imageData) {
   1.550 +              PostDataError();
   1.551 +              return;
   1.552 +            }
   1.553 +            uint32_t* decoded = imageData + mCurLine * GetRealWidth();
   1.554 +            uint32_t* decoded_end = decoded + GetRealWidth();
   1.555 +            uint8_t* p = mRow, *p_end = mRow + rowSize; 
   1.556 +            while (p < p_end) {
   1.557 +              uint8_t idx = *p++;
   1.558 +              for (uint8_t bit = 0x80; bit && decoded<decoded_end; bit >>= 1) {
   1.559 +                // Clear pixel completely for transparency.
   1.560 +                if (idx & bit) {
   1.561 +                  *decoded = 0;
   1.562 +                }
   1.563 +                decoded++;
   1.564 +              }
   1.565 +            }
   1.566 +          }
   1.567 +        }
   1.568 +      }
   1.569 +    }
   1.570 +  }
   1.571 +}
   1.572 +
   1.573 +bool
   1.574 +nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
   1.575 +{
   1.576 +  mContainedDecoder->Write(aBuffer, aCount, aStrategy);
   1.577 +  if (mContainedDecoder->HasDataError()) {
   1.578 +    mDataError = mContainedDecoder->HasDataError();
   1.579 +  }
   1.580 +  if (mContainedDecoder->HasDecoderError()) {
   1.581 +    PostDecoderError(mContainedDecoder->GetDecoderError());
   1.582 +  }
   1.583 +  return !HasError();
   1.584 +}
   1.585 +
   1.586 +void
   1.587 +nsICODecoder::ProcessDirEntry(IconDirEntry& aTarget)
   1.588 +{
   1.589 +  memset(&aTarget, 0, sizeof(aTarget));
   1.590 +  memcpy(&aTarget.mWidth, mDirEntryArray, sizeof(aTarget.mWidth));
   1.591 +  memcpy(&aTarget.mHeight, mDirEntryArray + 1, sizeof(aTarget.mHeight));
   1.592 +  memcpy(&aTarget.mColorCount, mDirEntryArray + 2, sizeof(aTarget.mColorCount));
   1.593 +  memcpy(&aTarget.mReserved, mDirEntryArray + 3, sizeof(aTarget.mReserved));
   1.594 +  memcpy(&aTarget.mPlanes, mDirEntryArray + 4, sizeof(aTarget.mPlanes));
   1.595 +  aTarget.mPlanes = LittleEndian::readUint16(&aTarget.mPlanes);
   1.596 +  memcpy(&aTarget.mBitCount, mDirEntryArray + 6, sizeof(aTarget.mBitCount));
   1.597 +  aTarget.mBitCount = LittleEndian::readUint16(&aTarget.mBitCount);
   1.598 +  memcpy(&aTarget.mBytesInRes, mDirEntryArray + 8, sizeof(aTarget.mBytesInRes));
   1.599 +  aTarget.mBytesInRes = LittleEndian::readUint32(&aTarget.mBytesInRes);
   1.600 +  memcpy(&aTarget.mImageOffset, mDirEntryArray + 12, 
   1.601 +         sizeof(aTarget.mImageOffset));
   1.602 +  aTarget.mImageOffset = LittleEndian::readUint32(&aTarget.mImageOffset);
   1.603 +}
   1.604 +
   1.605 +bool
   1.606 +nsICODecoder::NeedsNewFrame() const
   1.607 +{
   1.608 +  if (mContainedDecoder) {
   1.609 +    return mContainedDecoder->NeedsNewFrame();
   1.610 +  }
   1.611 +
   1.612 +  return Decoder::NeedsNewFrame();
   1.613 +}
   1.614 +
   1.615 +nsresult
   1.616 +nsICODecoder::AllocateFrame()
   1.617 +{
   1.618 +  if (mContainedDecoder) {
   1.619 +    nsresult rv = mContainedDecoder->AllocateFrame();
   1.620 +    mCurrentFrame = mContainedDecoder->GetCurrentFrame();
   1.621 +    return rv;
   1.622 +  }
   1.623 +
   1.624 +  return Decoder::AllocateFrame();
   1.625 +}
   1.626 +
   1.627 +} // namespace image
   1.628 +} // namespace mozilla

mercurial