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