// Returns a buffer filled with the bitmap file header in little endian: // Signature 2 bytes 'BM' // FileSize 4 bytes File size in bytes // reserved 4 bytes unused (=0) // DataOffset 4 bytes File offset to Raster Data // Returns true if successful bool nsICODecoder::FillBitmapFileHeaderBuffer(PRInt8 *bfh) { memset(bfh, 0, 14); bfh[0] = 'B'; bfh[1] = 'M'; PRInt32 dataOffset = 0; PRInt32 fileSize = 0; dataOffset = BFH_LENGTH + BITMAPINFOSIZE; // The color table is present only if BPP is <= 8 if (mDirEntry.mBitCount <= 8) { PRUint16 numColors = GetNumColors(); if (numColors == (PRUint16)-1) { return false; } dataOffset += 4 * numColors; fileSize = dataOffset + GetRealWidth() * GetRealHeight(); } else { fileSize = dataOffset + (mDirEntry.mBitCount * GetRealWidth() * GetRealHeight()) / 8; } fileSize = NATIVE32_TO_LITTLE(fileSize); memcpy(bfh + 2, &fileSize, sizeof(fileSize)); dataOffset = NATIVE32_TO_LITTLE(dataOffset); memcpy(bfh + 10, &dataOffset, sizeof(dataOffset)); return true; }
BOOL FADIB::CreateDIB() { CMainFrame * pFrame = (CMainFrame *) AfxGetApp()->m_pMainWnd; int colorNum = 256; dibStep = GetRealWidth(pFrame->fileopen->width); // Calculate DIB size int dibSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * colorNum + dibStep * pFrame->fileopen->height; // Allocate DIB memory if(dibData) delete[] dibData; dibData = new unsigned char[dibSize]; bitmapInfo = (BITMAPINFO *) dibData; // Make BITMAPINFOHEADER bitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmapInfo->bmiHeader.biWidth = pFrame->fileopen->width; bitmapInfo->bmiHeader.biHeight = pFrame->fileopen->height; bitmapInfo->bmiHeader.biPlanes = 1; bitmapInfo->bmiHeader.biBitCount = WORD(pFrame->fileopen->samplePerPixel *8); bitmapInfo->bmiHeader.biCompression = 0; bitmapInfo->bmiHeader.biSizeImage = dibStep * pFrame->fileopen->height; bitmapInfo->bmiHeader.biXPelsPerMeter = 0; bitmapInfo->bmiHeader.biYPelsPerMeter = 0; bitmapInfo->bmiHeader.biClrUsed = colorNum; bitmapInfo->bmiHeader.biClrImportant = 0; faImage = dibData + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * colorNum; if(pFrame->fileopen->photometric == MONOCHROME1) { for(int i=0;i<colorNum;i++) { bitmapInfo->bmiColors[i].rgbRed = 255-i; bitmapInfo->bmiColors[i].rgbGreen = 255-i; bitmapInfo->bmiColors[i].rgbBlue = 255-i; bitmapInfo->bmiColors[i].rgbReserved = 0; } } else if(pFrame->fileopen->photometric == MONOCHROME2) { for(int i=0;i<colorNum;i++) { bitmapInfo->bmiColors[i].rgbRed = i; bitmapInfo->bmiColors[i].rgbGreen = i; bitmapInfo->bmiColors[i].rgbBlue = i; bitmapInfo->bmiColors[i].rgbReserved = 0; } } FATrans16to8(); return TRUE; }
void CChildView::OnFileopen() { // Show File Dialog Box CString szFilter = _T ("bitmap Files (*.bmp)|*.bmp|All Files (*.*)|*.*|"); CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_ALLOWMULTISELECT, szFilter, this); if(dlg.DoModal()==IDCANCEL) return; // open File FILE *file; _wfopen_s(&file, dlg.GetPathName(), L"rb"); // Read File Header BITMAPFILEHEADER bitmapFileHeader; fread(&bitmapFileHeader,sizeof(BITMAPFILEHEADER),1,file); // Calculate DIB size int dibSize = bitmapFileHeader .bfSize - sizeof (BITMAPFILEHEADER); // Allocate DIB size if( dibData) delete [] dibData; dibData = new unsigned char[ dibSize ]; // Read DIB fread( dibData, dibSize, 1, file ); // Important Variables bitmapInfo = (BITMAPINFO *) dibData; samplePerPixel = bitmapInfo->bmiHeader.biBitCount/8; imageWidth = bitmapInfo->bmiHeader.biWidth; imageHeight = bitmapInfo->bmiHeader.biHeight; imageStep = GetRealWidth (imageWidth); srcData = dibData + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * bitmapInfo->bmiHeader.biClrUsed; // Allocate destination memory if( dstData ) delete [] dstData; dstData = new unsigned char[ imageStep * imageHeight ]; // Copy src to dst memcpy( dstData, srcData, imageStep * imageHeight ); // Close file fclose( file ); // Repaint client area Invalidate (FALSE); }
BOOL CChildView::CreateDIB() { int colorNum = 256; dibStep = GetRealWidth(width); //Calculate DIB size int dibSize = sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*colorNum+dibStep*height; //Allocated DIB size if(dibData) delete[] dibData; dibData = new unsigned char[dibSize]; bitmapInfo = (BITMAPINFO*) dibData; //Make BITMAPINFOHEADER bitmapInfo ->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmapInfo ->bmiHeader.biWidth = width; bitmapInfo ->bmiHeader.biHeight = height; bitmapInfo ->bmiHeader.biPlanes = 1; bitmapInfo ->bmiHeader.biBitCount = WORD(samplePerPixel*8); bitmapInfo ->bmiHeader.biCompression = 0; bitmapInfo ->bmiHeader.biSizeImage = dibStep*height; bitmapInfo ->bmiHeader.biXPelsPerMeter = 0; bitmapInfo ->bmiHeader.biYPelsPerMeter = 0; bitmapInfo ->bmiHeader.biClrUsed = colorNum; bitmapInfo ->bmiHeader.biClrImportant = 0; dibImage = dibData+sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*colorNum; if(photometric == MONOCHROME1) { for(int i = 0; i<colorNum; i++) { bitmapInfo->bmiColors[i].rgbRed = 255-i; bitmapInfo->bmiColors[i].rgbGreen = 255-i; bitmapInfo->bmiColors[i].rgbBlue = 255-i; bitmapInfo->bmiColors[i].rgbReserved = 0; } } else if(photometric == MONOCHROME2) { for(int i = 0; i<colorNum; i++) { bitmapInfo->bmiColors[i].rgbRed = i; bitmapInfo->bmiColors[i].rgbGreen = i; bitmapInfo->bmiColors[i].rgbBlue = i; bitmapInfo->bmiColors[i].rgbReserved = 0; } } return TRUE; }
void CChildView::OpenBITMAPFile(CString path) { FILE *file; _wfopen_s(&file, path, L"rb"); BITMAPFILEHEADER bitmapFileHeader; fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, file); int dibSize = bitmapFileHeader.bfSize-sizeof(BITMAPFILEHEADER); if(dibData) delete[] dibData; dibData = new unsigned char[dibSize]; fread(dibData, dibSize, 1, file); //Important Variables bitmapInfo = (BITMAPINFO *) dibData; samplePerPixel = bitmapInfo->bmiHeader.biBitCount/8; width = bitmapInfo->bmiHeader.biWidth; height = bitmapInfo->bmiHeader.biHeight; srcStep = GetRealWidth(width); srcData = dibData + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * bitmapInfo->bmiHeader.biClrUsed; //Allocate destination memory if(dstData) delete[] dstData; dstData = new unsigned char[srcStep*height]; memcpy(dstData, srcData, srcStep*height); memcpy(dibImage, dstData, srcStep*height); //close file fclose(file); //Repaint client area Invalidate(FALSE); }
void nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy) { NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); if (!aCount) { if (mContainedDecoder) { WriteToContainedDecoder(aBuffer, aCount, aStrategy); } return; } while (aCount && (mPos < ICONCOUNTOFFSET)) { // Skip to the # of icons. if (mPos == 2) { // if the third byte is 1: This is an icon, 2: a cursor if ((*aBuffer != 1) && (*aBuffer != 2)) { PostDataError(); return; } mIsCursor = (*aBuffer == 2); } mPos++; aBuffer++; aCount--; } if (mPos == ICONCOUNTOFFSET && aCount >= 2) { mNumIcons = LittleEndian::readUint16(reinterpret_cast<const uint16_t*>(aBuffer)); aBuffer += 2; mPos += 2; aCount -= 2; } if (mNumIcons == 0) return; // Nothing to do. uint16_t colorDepth = 0; nsIntSize prefSize = mImage.GetRequestedResolution(); if (prefSize.width == 0 && prefSize.height == 0) { prefSize.SizeTo(PREFICONSIZE, PREFICONSIZE); } // A measure of the difference in size between the entry we've found // and the requested size. We will choose the smallest image that is // >= requested size (i.e. we assume it's better to downscale a larger // icon than to upscale a smaller one). int32_t diff = INT_MIN; // Loop through each entry's dir entry while (mCurrIcon < mNumIcons) { if (mPos >= DIRENTRYOFFSET + (mCurrIcon * sizeof(mDirEntryArray)) && mPos < DIRENTRYOFFSET + ((mCurrIcon + 1) * sizeof(mDirEntryArray))) { uint32_t toCopy = sizeof(mDirEntryArray) - (mPos - DIRENTRYOFFSET - mCurrIcon * sizeof(mDirEntryArray)); if (toCopy > aCount) { toCopy = aCount; } memcpy(mDirEntryArray + sizeof(mDirEntryArray) - toCopy, aBuffer, toCopy); mPos += toCopy; aCount -= toCopy; aBuffer += toCopy; } if (aCount == 0) return; // Need more data IconDirEntry e; if (mPos == (DIRENTRYOFFSET + ICODIRENTRYSIZE) + (mCurrIcon * sizeof(mDirEntryArray))) { mCurrIcon++; ProcessDirEntry(e); // We can't use GetRealWidth and GetRealHeight here because those operate // on mDirEntry, here we are going through each item in the directory. // Calculate the delta between this image's size and the desired size, // so we can see if it is better than our current-best option. // In the case of several equally-good images, we use the last one. int32_t delta = (e.mWidth == 0 ? 256 : e.mWidth) - prefSize.width + (e.mHeight == 0 ? 256 : e.mHeight) - prefSize.height; if (e.mBitCount >= colorDepth && ((diff < 0 && delta >= diff) || (delta >= 0 && delta <= diff))) { diff = delta; mImageOffset = e.mImageOffset; // ensure mImageOffset is >= size of the direntry headers (bug #245631) uint32_t minImageOffset = DIRENTRYOFFSET + mNumIcons * sizeof(mDirEntryArray); if (mImageOffset < minImageOffset) { PostDataError(); return; } colorDepth = e.mBitCount; memcpy(&mDirEntry, &e, sizeof(IconDirEntry)); } } } if (mPos < mImageOffset) { // Skip to (or at least towards) the desired image offset uint32_t toSkip = mImageOffset - mPos; if (toSkip > aCount) toSkip = aCount; mPos += toSkip; aBuffer += toSkip; aCount -= toSkip; } // If we are within the first PNGSIGNATURESIZE bytes of the image data, // then we have either a BMP or a PNG. We use the first PNGSIGNATURESIZE // bytes to determine which one we have. if (mCurrIcon == mNumIcons && mPos >= mImageOffset && mPos < mImageOffset + PNGSIGNATURESIZE) { uint32_t toCopy = PNGSIGNATURESIZE - (mPos - mImageOffset); if (toCopy > aCount) { toCopy = aCount; } memcpy(mSignature + (mPos - mImageOffset), aBuffer, toCopy); mPos += toCopy; aCount -= toCopy; aBuffer += toCopy; mIsPNG = !memcmp(mSignature, nsPNGDecoder::pngSignatureBytes, PNGSIGNATURESIZE); if (mIsPNG) { mContainedDecoder = new nsPNGDecoder(mImage); mContainedDecoder->SetObserver(mObserver); mContainedDecoder->SetSizeDecode(IsSizeDecode()); mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength, mColormap, mColormapSize, mCurrentFrame); if (!WriteToContainedDecoder(mSignature, PNGSIGNATURESIZE, aStrategy)) { return; } } } // If we have a PNG, let the PNG decoder do all of the rest of the work if (mIsPNG && mContainedDecoder && mPos >= mImageOffset + PNGSIGNATURESIZE) { if (!WriteToContainedDecoder(aBuffer, aCount, aStrategy)) { return; } if (!HasSize() && mContainedDecoder->HasSize()) { PostSize(mContainedDecoder->GetImageMetadata().GetWidth(), mContainedDecoder->GetImageMetadata().GetHeight()); } mPos += aCount; aBuffer += aCount; aCount = 0; // Raymond Chen says that 32bpp only are valid PNG ICOs // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx if (!IsSizeDecode() && !static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) { PostDataError(); } return; } // We've processed all of the icon dir entries and are within the // bitmap info size if (!mIsPNG && mCurrIcon == mNumIcons && mPos >= mImageOffset && mPos >= mImageOffset + PNGSIGNATURESIZE && mPos < mImageOffset + BITMAPINFOSIZE) { // As we were decoding, we did not know if we had a PNG signature or the // start of a bitmap information header. At this point we know we had // a bitmap information header and not a PNG signature, so fill the bitmap // information header with the data it should already have. memcpy(mBIHraw, mSignature, PNGSIGNATURESIZE); // We've found the icon. uint32_t toCopy = sizeof(mBIHraw) - (mPos - mImageOffset); if (toCopy > aCount) toCopy = aCount; memcpy(mBIHraw + (mPos - mImageOffset), aBuffer, toCopy); mPos += toCopy; aCount -= toCopy; aBuffer += toCopy; } // If we have a BMP inside the ICO and we have read the BIH header if (!mIsPNG && mPos == mImageOffset + BITMAPINFOSIZE) { // Make sure we have a sane value for the bitmap information header int32_t bihSize = ExtractBIHSizeFromBitmap(reinterpret_cast<int8_t*>(mBIHraw)); if (bihSize != BITMAPINFOSIZE) { PostDataError(); return; } // We are extracting the BPP from the BIH header as it should be trusted // over the one we have from the icon header mBPP = ExtractBPPFromBitmap(reinterpret_cast<int8_t*>(mBIHraw)); // Init the bitmap decoder which will do most of the work for us // It will do everything except the AND mask which isn't present in bitmaps // bmpDecoder is for local scope ease, it will be freed by mContainedDecoder nsBMPDecoder *bmpDecoder = new nsBMPDecoder(mImage); mContainedDecoder = bmpDecoder; bmpDecoder->SetUseAlphaData(true); mContainedDecoder->SetObserver(mObserver); mContainedDecoder->SetSizeDecode(IsSizeDecode()); mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength, mColormap, mColormapSize, mCurrentFrame); // The ICO format when containing a BMP does not include the 14 byte // bitmap file header. To use the code of the BMP decoder we need to // generate this header ourselves and feed it to the BMP decoder. int8_t bfhBuffer[BMPFILEHEADERSIZE]; if (!FillBitmapFileHeaderBuffer(bfhBuffer)) { PostDataError(); return; } if (!WriteToContainedDecoder((const char*)bfhBuffer, sizeof(bfhBuffer), aStrategy)) { return; } // Setup the cursor hot spot if one is present SetHotSpotIfCursor(); // Fix the ICO height from the BIH. // Fix the height on the BIH to be /2 so our BMP decoder will understand. if (!FixBitmapHeight(reinterpret_cast<int8_t*>(mBIHraw))) { PostDataError(); return; } // Fix the ICO width from the BIH. if (!FixBitmapWidth(reinterpret_cast<int8_t*>(mBIHraw))) { PostDataError(); return; } // Write out the BMP's bitmap info header if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw), aStrategy)) { return; } PostSize(mContainedDecoder->GetImageMetadata().GetWidth(), mContainedDecoder->GetImageMetadata().GetHeight()); // We have the size. If we're doing a size decode, we got what // we came for. if (IsSizeDecode()) return; // Sometimes the ICO BPP header field is not filled out // so we should trust the contained resource over our own // information. mBPP = bmpDecoder->GetBitsPerPixel(); // Check to make sure we have valid color settings uint16_t numColors = GetNumColors(); if (numColors == (uint16_t)-1) { PostDataError(); return; } } // If we have a BMP if (!mIsPNG && mContainedDecoder && mPos >= mImageOffset + BITMAPINFOSIZE) { uint16_t numColors = GetNumColors(); if (numColors == (uint16_t)-1) { PostDataError(); return; } // Feed the actual image data (not including headers) into the BMP decoder uint32_t bmpDataOffset = mDirEntry.mImageOffset + BITMAPINFOSIZE; uint32_t bmpDataEnd = mDirEntry.mImageOffset + BITMAPINFOSIZE + static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetCompressedImageSize() + 4 * numColors; // If we are feeding in the core image data, but we have not yet // reached the ICO's 'AND buffer mask' if (mPos >= bmpDataOffset && mPos < bmpDataEnd) { // Figure out how much data the BMP decoder wants uint32_t toFeed = bmpDataEnd - mPos; if (toFeed > aCount) { toFeed = aCount; } if (!WriteToContainedDecoder(aBuffer, toFeed, aStrategy)) { return; } mPos += toFeed; aCount -= toFeed; aBuffer += toFeed; } // If the bitmap is fully processed, treat any left over data as the ICO's // 'AND buffer mask' which appears after the bitmap resource. if (!mIsPNG && mPos >= bmpDataEnd) { // There may be an optional AND bit mask after the data. This is // only used if the alpha data is not already set. The alpha data // is used for 32bpp bitmaps as per the comment in ICODecoder.h // The alpha mask should be checked in all other cases. if (static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetBitsPerPixel() != 32 || !static_cast<nsBMPDecoder*>(mContainedDecoder.get())->HasAlphaData()) { uint32_t rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up if (mPos == bmpDataEnd) { mPos++; mRowBytes = 0; mCurLine = GetRealHeight(); mRow = (uint8_t*)moz_realloc(mRow, rowSize); if (!mRow) { PostDecoderError(NS_ERROR_OUT_OF_MEMORY); return; } } // Ensure memory has been allocated before decoding. NS_ABORT_IF_FALSE(mRow, "mRow is null"); if (!mRow) { PostDataError(); return; } while (mCurLine > 0 && aCount > 0) { uint32_t toCopy = std::min(rowSize - mRowBytes, aCount); if (toCopy) { memcpy(mRow + mRowBytes, aBuffer, toCopy); aCount -= toCopy; aBuffer += toCopy; mRowBytes += toCopy; } if (rowSize == mRowBytes) { mCurLine--; mRowBytes = 0; uint32_t* imageData = static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetImageData(); if (!imageData) { PostDataError(); return; } uint32_t* decoded = imageData + mCurLine * GetRealWidth(); uint32_t* decoded_end = decoded + GetRealWidth(); uint8_t* p = mRow, *p_end = mRow + rowSize; while (p < p_end) { uint8_t idx = *p++; for (uint8_t bit = 0x80; bit && decoded<decoded_end; bit >>= 1) { // Clear pixel completely for transparency. if (idx & bit) { *decoded = 0; } decoded++; } } } } }
void nsICODecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) { NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); if (!aCount) // aCount=0 means EOF return; while (aCount && (mPos < ICONCOUNTOFFSET)) { // Skip to the # of icons. if (mPos == 2) { // if the third byte is 1: This is an icon, 2: a cursor if ((*aBuffer != 1) && (*aBuffer != 2)) { PostDataError(); return; } mIsCursor = (*aBuffer == 2); } mPos++; aBuffer++; aCount--; } if (mPos == ICONCOUNTOFFSET && aCount >= 2) { mNumIcons = LITTLE_TO_NATIVE16(((PRUint16*)aBuffer)[0]); aBuffer += 2; mPos += 2; aCount -= 2; } if (mNumIcons == 0) return; // Nothing to do. PRUint16 colorDepth = 0; // Loop through each entry's dir entry while (mCurrIcon < mNumIcons) { if (mPos >= DIRENTRYOFFSET + (mCurrIcon * sizeof(mDirEntryArray)) && mPos < DIRENTRYOFFSET + ((mCurrIcon + 1) * sizeof(mDirEntryArray))) { PRUint32 toCopy = sizeof(mDirEntryArray) - (mPos - DIRENTRYOFFSET - mCurrIcon * sizeof(mDirEntryArray)); if (toCopy > aCount) { toCopy = aCount; } memcpy(mDirEntryArray + sizeof(mDirEntryArray) - toCopy, aBuffer, toCopy); mPos += toCopy; aCount -= toCopy; aBuffer += toCopy; } if (aCount == 0) return; // Need more data IconDirEntry e; if (mPos == (DIRENTRYOFFSET + ICODIRENTRYSIZE) + (mCurrIcon * sizeof(mDirEntryArray))) { mCurrIcon++; ProcessDirEntry(e); // We can't use GetRealWidth and GetRealHeight here because those operate // on mDirEntry, here we are going through each item in the directory if (((e.mWidth == 0 ? 256 : e.mWidth) == PREFICONSIZE && (e.mHeight == 0 ? 256 : e.mHeight) == PREFICONSIZE && (e.mBitCount >= colorDepth)) || (mCurrIcon == mNumIcons && mImageOffset == 0)) { mImageOffset = e.mImageOffset; // ensure mImageOffset is >= size of the direntry headers (bug #245631) PRUint32 minImageOffset = DIRENTRYOFFSET + mNumIcons * sizeof(mDirEntryArray); if (mImageOffset < minImageOffset) { PostDataError(); return; } colorDepth = e.mBitCount; memcpy(&mDirEntry, &e, sizeof(IconDirEntry)); } } } if (mPos < mImageOffset) { // Skip to (or at least towards) the desired image offset PRUint32 toSkip = mImageOffset - mPos; if (toSkip > aCount) toSkip = aCount; mPos += toSkip; aBuffer += toSkip; aCount -= toSkip; } // If we are within the first PNGSIGNATURESIZE bytes of the image data, // then we have either a BMP or a PNG. We use the first PNGSIGNATURESIZE // bytes to determine which one we have. if (mCurrIcon == mNumIcons && mPos >= mImageOffset && mPos < mImageOffset + PNGSIGNATURESIZE) { PRUint32 toCopy = PNGSIGNATURESIZE - (mPos - mImageOffset); if (toCopy > aCount) { toCopy = aCount; } memcpy(mSignature + (mPos - mImageOffset), aBuffer, toCopy); mPos += toCopy; aCount -= toCopy; aBuffer += toCopy; mIsPNG = !memcmp(mSignature, nsPNGDecoder::pngSignatureBytes, PNGSIGNATURESIZE); if (mIsPNG) { mContainedDecoder = new nsPNGDecoder(); mContainedDecoder->InitSharedDecoder(mImage, mObserver); mContainedDecoder->Write(mSignature, PNGSIGNATURESIZE); mDataError = mContainedDecoder->HasDataError(); if (mContainedDecoder->HasDataError()) { return; } } } // If we have a PNG, let the PNG decoder do all of the rest of the work if (mIsPNG && mContainedDecoder && mPos >= mImageOffset + PNGSIGNATURESIZE) { mContainedDecoder->Write(aBuffer, aCount); mDataError = mContainedDecoder->HasDataError(); if (mContainedDecoder->HasDataError()) { return; } mPos += aCount; aBuffer += aCount; aCount = 0; // Raymond Chen says that 32bpp only are valid PNG ICOs // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx if (static_cast<nsPNGDecoder*>(mContainedDecoder.get())->HasValidInfo() && static_cast<nsPNGDecoder*>(mContainedDecoder.get())->GetPixelDepth() != 32) { PostDataError(); } return; } // We've processed all of the icon dir entries and are within the // bitmap info size if (!mIsPNG && mCurrIcon == mNumIcons && mPos >= mImageOffset && mPos >= mImageOffset + PNGSIGNATURESIZE && mPos < mImageOffset + BITMAPINFOSIZE) { // As we were decoding, we did not know if we had a PNG signature or the // start of a bitmap information header. At this point we know we had // a bitmap information header and not a PNG signature, so fill the bitmap // information header with the data it should already have. memcpy(mBIHraw, mSignature, PNGSIGNATURESIZE); // We've found the icon. PRUint32 toCopy = sizeof(mBIHraw) - (mPos - mImageOffset); if (toCopy > aCount) toCopy = aCount; memcpy(mBIHraw + (mPos - mImageOffset), aBuffer, toCopy); mPos += toCopy; aCount -= toCopy; aBuffer += toCopy; } // If we have a BMP inside the ICO and we have read the BIH header if (!mIsPNG && mPos == mImageOffset + BITMAPINFOSIZE) { // Make sure we have a sane value for the bitmap information header PRInt32 bihSize = ExtractBIHSizeFromBitmap(reinterpret_cast<PRInt8*>(mBIHraw)); if (bihSize != BITMAPINFOSIZE) { PostDataError(); return; } // We are extracting the BPP from the BIH header as it should be trusted // over the one we have from the icon header mBPP = ExtractBPPFromBitmap(reinterpret_cast<PRInt8*>(mBIHraw)); // Init the bitmap decoder which will do most of the work for us // It will do everything except the AND mask which isn't present in bitmaps // bmpDecoder is for local scope ease, it will be freed by mContainedDecoder nsBMPDecoder *bmpDecoder = new nsBMPDecoder(); mContainedDecoder = bmpDecoder; bmpDecoder->SetUseAlphaData(PR_TRUE); mContainedDecoder->SetSizeDecode(IsSizeDecode()); mContainedDecoder->InitSharedDecoder(mImage, mObserver); // The ICO format when containing a BMP does not include the 14 byte // bitmap file header. To use the code of the BMP decoder we need to // generate this header ourselves and feed it to the BMP decoder. PRInt8 bfhBuffer[BMPFILEHEADERSIZE]; if (!FillBitmapFileHeaderBuffer(bfhBuffer)) { PostDataError(); return; } mContainedDecoder->Write((const char*)bfhBuffer, sizeof(bfhBuffer)); mDataError = mContainedDecoder->HasDataError(); if (mContainedDecoder->HasDataError()) { return; } // Setup the cursor hot spot if one is present SetHotSpotIfCursor(); // Fix the height on the BMP resource FillBitmapInformationBufferHeight((PRInt8*)mBIHraw); // Write out the BMP's bitmap info header mContainedDecoder->Write(mBIHraw, sizeof(mBIHraw)); mDataError = mContainedDecoder->HasDataError(); if (mContainedDecoder->HasDataError()) { return; } // We have the size. If we're doing a size decode, we got what // we came for. if (IsSizeDecode()) return; // Sometimes the ICO BPP header field is not filled out // so we should trust the contained resource over our own // information. mBPP = bmpDecoder->GetBitsPerPixel(); // Check to make sure we have valid color settings PRUint16 numColors = GetNumColors(); if (numColors == (PRUint16)-1) { PostDataError(); return; } } // If we have a BMP if (!mIsPNG && mContainedDecoder && mPos >= mImageOffset + BITMAPINFOSIZE) { PRUint16 numColors = GetNumColors(); if (numColors == (PRUint16)-1) { PostDataError(); return; } // Feed the actual image data (not including headers) into the BMP decoder PRInt32 bmpDataOffset = mDirEntry.mImageOffset + BITMAPINFOSIZE; PRInt32 bmpDataEnd = mDirEntry.mImageOffset + BITMAPINFOSIZE + static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetCompressedImageSize() + 4 * numColors; // If we are feeding in the core image data, but we have not yet // reached the ICO's 'AND buffer mask' if (mPos >= bmpDataOffset && mPos < bmpDataEnd) { // Figure out how much data the BMP decoder wants PRUint32 toFeed = bmpDataEnd - mPos; if (toFeed > aCount) { toFeed = aCount; } mContainedDecoder->Write(aBuffer, toFeed); mDataError = mContainedDecoder->HasDataError(); if (mContainedDecoder->HasDataError()) { return; } mPos += toFeed; aCount -= toFeed; aBuffer += toFeed; } // If the bitmap is fully processed, treat any left over data as the ICO's // 'AND buffer mask' which appears after the bitmap resource. if (!mIsPNG && mPos >= bmpDataEnd) { // There may be an optional AND bit mask after the data. This is // only used if the alpha data is not already set. The alpha data // is used for 32bpp bitmaps as per the comment in ICODecoder.h // The alpha mask should be checked in all other cases. if (static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetBitsPerPixel() != 32 || !static_cast<nsBMPDecoder*>(mContainedDecoder.get())->HasAlphaData()) { PRUint32 rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up if (mPos == bmpDataEnd) { mPos++; mRowBytes = 0; mCurLine = GetRealHeight(); mRow = (PRUint8*)moz_realloc(mRow, rowSize); if (!mRow) { PostDecoderError(NS_ERROR_OUT_OF_MEMORY); return; } } // Ensure memory has been allocated before decoding. NS_ABORT_IF_FALSE(mRow, "mRow is null"); NS_ABORT_IF_FALSE(mImage, "mImage is null"); if (!mRow || !mImage) { PostDataError(); return; } while (mCurLine > 0 && aCount > 0) { PRUint32 toCopy = NS_MIN(rowSize - mRowBytes, aCount); if (toCopy) { memcpy(mRow + mRowBytes, aBuffer, toCopy); aCount -= toCopy; aBuffer += toCopy; mRowBytes += toCopy; } if (rowSize == mRowBytes) { mCurLine--; mRowBytes = 0; PRUint32* imageData = static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetImageData(); if (!imageData) { PostDataError(); return; } PRUint32* decoded = imageData + mCurLine * GetRealWidth(); PRUint32* decoded_end = decoded + GetRealWidth(); PRUint8* p = mRow, *p_end = mRow + rowSize; while (p < p_end) { PRUint8 idx = *p++; for (PRUint8 bit = 0x80; bit && decoded<decoded_end; bit >>= 1) { // Clear pixel completely for transparency. if (idx & bit) { *decoded = 0; } decoded++; } } } } }