From e8392a91ce32ae1cf8353f6a4d3c7cc12603ce7f Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Thu, 25 Aug 2011 16:09:01 -0400 Subject: [PATCH] Bug 600556 - Support Vista-style PNG ICO files. r=joe --- modules/libpr0n/decoders/nsBMPDecoder.cpp | 186 +++++-- modules/libpr0n/decoders/nsBMPDecoder.h | 54 +- modules/libpr0n/decoders/nsICODecoder.cpp | 639 +++++++++++----------- modules/libpr0n/decoders/nsICODecoder.h | 61 ++- modules/libpr0n/decoders/nsPNGDecoder.cpp | 10 +- modules/libpr0n/decoders/nsPNGDecoder.h | 19 + modules/libpr0n/src/Decoder.cpp | 28 + modules/libpr0n/src/Decoder.h | 34 +- 8 files changed, 643 insertions(+), 388 deletions(-) diff --git a/modules/libpr0n/decoders/nsBMPDecoder.cpp b/modules/libpr0n/decoders/nsBMPDecoder.cpp index c7efe5ae72b..a35be69c32c 100644 --- a/modules/libpr0n/decoders/nsBMPDecoder.cpp +++ b/modules/libpr0n/decoders/nsBMPDecoder.cpp @@ -22,6 +22,7 @@ * Contributor(s): * Neil Rashbrook * Bobby Holley + * Brian R. Bondy * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -70,13 +71,73 @@ nsBMPDecoder::nsBMPDecoder() mState = eRLEStateInitial; mStateData = 0; mLOH = WIN_HEADER_LENGTH; + mUseAlphaData = PR_FALSE; } nsBMPDecoder::~nsBMPDecoder() { delete[] mColors; - if (mRow) - free(mRow); + if (mRow) { + moz_free(mRow); + } +} + +// Sets whether or not the BMP will use alpha data +void +nsBMPDecoder::SetUseAlphaData(PRBool useAlphaData) +{ + mUseAlphaData = useAlphaData; +} + +// Obtains the bits per pixel from the internal BIH header +PRInt32 +nsBMPDecoder::GetBitsPerPixel() const +{ + return mBIH.bpp; +} + +// Obtains the width from the internal BIH header +PRInt32 +nsBMPDecoder::GetWidth() const +{ + return mBIH.width; +} + +// Obtains the height from the internal BIH header +PRInt32 +nsBMPDecoder::GetHeight() const +{ + return mBIH.height; +} + +// Obtains the internal output image buffer +PRUint32* +nsBMPDecoder::GetImageData() +{ + return mImageData; +} + +// Obtains the size of the compressed image resource +PRInt32 +nsBMPDecoder::GetCompressedImageSize() const +{ + // For everything except BI_RGB the header field must be defined + if (mBIH.compression != BI_RGB) { + return mBIH.image_size; + } + + // mBIH.image_size isn't always filled for BI_RGB so calculate it manually + // The pixel array size is calculated based on extra 4 byte boundary padding + PRUint32 rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up + // Pad to DWORD Boundary + if (rowSize % 4) { + rowSize += (4 - (rowSize % 4)); + } + + // The height should be the absolute value of what the height is in the BIH. + // If positive the bitmap is stored bottom to top, otherwise top to bottom + PRInt32 pixelArraySize = rowSize * abs(mBIH.height); + return pixelArraySize; } void @@ -90,6 +151,11 @@ nsBMPDecoder::FinishInternal() // Send notifications if appropriate if (!IsSizeDecode() && (GetFrameCount() == 1)) { + + // Invalidate + nsIntRect r(0, 0, mBIH.width, mBIH.height); + PostInvalidation(r); + PostFrameStop(); PostDecodeDone(); } @@ -145,8 +211,8 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) return; nsresult rv; - if (mPos < BFH_LENGTH) { /* In BITMAPFILEHEADER */ - PRUint32 toCopy = BFH_LENGTH - mPos; + if (mPos < BFH_INTERNAL_LENGTH) { /* In BITMAPFILEHEADER */ + PRUint32 toCopy = BFH_INTERNAL_LENGTH - mPos; if (toCopy > aCount) toCopy = aCount; memcpy(mRawBuf + mPos, aBuffer, toCopy); @@ -154,7 +220,7 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) aCount -= toCopy; aBuffer += toCopy; } - if (mPos == BFH_LENGTH) { + if (mPos == BFH_INTERNAL_LENGTH) { ProcessFileHeader(); if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M') { PostDataError(); @@ -163,19 +229,23 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) if (mBFH.bihsize == OS2_BIH_LENGTH) mLOH = OS2_HEADER_LENGTH; } - if (mPos >= BFH_LENGTH && mPos < mLOH) { /* In BITMAPINFOHEADER */ + if (mPos >= BFH_INTERNAL_LENGTH && mPos < mLOH) { /* In BITMAPINFOHEADER */ PRUint32 toCopy = mLOH - mPos; if (toCopy > aCount) toCopy = aCount; - memcpy(mRawBuf + (mPos - BFH_LENGTH), aBuffer, toCopy); + memcpy(mRawBuf + (mPos - BFH_INTERNAL_LENGTH), aBuffer, toCopy); mPos += toCopy; aCount -= toCopy; aBuffer += toCopy; } - if (mPos == mLOH) { + + // GetNumFrames is called to ensure that if at this point mPos == mLOH but + // we have no data left to process, the next time WriteInternal is called + // we won't enter this condition again. + if (mPos == mLOH && GetFrameCount() == 0) { ProcessInfoHeader(); - PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP image is %lix%lix%lu. compression=%lu\n", - mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression)); + PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP is %lix%lix%lu. compression=%lu\n", + mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression)); // Verify we support this bit depth if (mBIH.bpp != 1 && mBIH.bpp != 4 && mBIH.bpp != 8 && mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32) { @@ -226,36 +296,70 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) CalcBitShift(); } + // Make sure we have a valid value for our supported compression modes + // before adding the frame + if (mBIH.compression != BI_RGB && mBIH.compression != BI_RLE8 && + mBIH.compression != BI_RLE4 && mBIH.compression != BI_BITFIELDS) { + PostDataError(); + return; + } + + // If we have RLE4 or RLE8 or BI_ALPHABITFIELDS, then ensure we + // have valid BPP values before adding the frame + if (mBIH.compression == BI_RLE8 && mBIH.bpp != 8) { + PR_LOG(gBMPLog, PR_LOG_DEBUG, + ("BMP RLE8 compression only supports 8 bits per pixel\n")); + PostDataError(); + return; + } + if (mBIH.compression == BI_RLE4 && mBIH.bpp != 4) { + PR_LOG(gBMPLog, PR_LOG_DEBUG, + ("BMP RLE4 compression only supports 4 bits per pixel\n")); + PostDataError(); + return; + } + if (mBIH.compression == BI_ALPHABITFIELDS && + mBIH.bpp != 16 && mBIH.bpp != 32) { + PR_LOG(gBMPLog, PR_LOG_DEBUG, + ("BMP ALPHABITFIELDS only supports 16 or 32 bits per pixel\n")); + PostDataError(); + return; + } + PRUint32 imageLength; - if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) { - rv = mImage->EnsureFrame(0, 0, 0, mBIH.width, real_height, gfxASurface::ImageFormatARGB32, + if (mBIH.compression == BI_RLE8 || mBIH.compression == BI_RLE4 || + mBIH.compression == BI_ALPHABITFIELDS) { + rv = mImage->EnsureFrame(0, 0, 0, mBIH.width, real_height, + gfxASurface::ImageFormatARGB32, (PRUint8**)&mImageData, &imageLength); } else { // mRow is not used for RLE encoded images - mRow = (PRUint8*)moz_malloc((mBIH.width * mBIH.bpp)/8 + 4); - // +4 because the line is padded to a 4 bit boundary, but I don't want + mRow = (PRUint8*)moz_malloc((mBIH.width * mBIH.bpp) / 8 + 4); + // + 4 because the line is padded to a 4 bit boundary, but I don't want // to make exact calculations here, that's unnecessary. // Also, it compensates rounding error. if (!mRow) { PostDecoderError(NS_ERROR_OUT_OF_MEMORY); return; } - rv = mImage->EnsureFrame(0, 0, 0, mBIH.width, real_height, gfxASurface::ImageFormatRGB24, - (PRUint8**)&mImageData, &imageLength); + + if (mUseAlphaData) { + rv = mImage->EnsureFrame(0, 0, 0, mBIH.width, real_height, + gfxASurface::ImageFormatARGB32, + (PRUint8**)&mImageData, &imageLength); + } else { + rv = mImage->EnsureFrame(0, 0, 0, mBIH.width, real_height, + gfxASurface::ImageFormatRGB24, + (PRUint8**)&mImageData, &imageLength); + } } if (NS_FAILED(rv) || !mImageData) { PostDecoderError(NS_ERROR_FAILURE); return; } - // Prepare for transparancy + // Prepare for transparency if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) { - if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) - || ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) { - PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n")); - PostDataError(); - return; - } // Clear the image, as the RLE may jump over areas memset(mImageData, 0, imageLength); } @@ -314,9 +418,10 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) // Need to increment mPos, else we might get to mPos==mLOH again // From now on, mPos is irrelevant if (!mBIH.compression || mBIH.compression == BI_BITFIELDS) { - PRUint32 rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // +7 to round up - if (rowSize % 4) + PRUint32 rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up + if (rowSize % 4) { rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary + } PRUint32 toCopy; do { toCopy = rowSize - mRowBytes; @@ -370,35 +475,44 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) p+=2; } break; - case 32: case 24: while (lpos > 0) { SetPixel(d, p[2], p[1], p[0]); p += 2; --lpos; - if (mBIH.bpp == 32) - p++; // Padding byte ++p; } break; + case 32: + while (lpos > 0) { + if (mUseAlphaData) { + SetPixel(d, p[2], p[1], p[0], p[3]); + } + else { + SetPixel(d, p[2], p[1], p[0]); + } + p += 4; + --lpos; + } + break; default: NS_NOTREACHED("Unsupported color depth, but earlier check didn't catch it"); } mCurLine --; if (mCurLine == 0) { // Finished last line - break; + break; } mRowBytes = 0; } } while (aCount > 0); - } + } else if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) { - if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) - || ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) { - PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n")); - PostDataError(); - return; + if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) || + ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) { + PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n")); + PostDataError(); + return; } while (aCount > 0) { @@ -562,7 +676,7 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) } } } - + const PRUint32 rows = mOldLine - mCurLine; if (rows) { diff --git a/modules/libpr0n/decoders/nsBMPDecoder.h b/modules/libpr0n/decoders/nsBMPDecoder.h index 0bd31fd0057..54526904ae1 100644 --- a/modules/libpr0n/decoders/nsBMPDecoder.h +++ b/modules/libpr0n/decoders/nsBMPDecoder.h @@ -21,6 +21,7 @@ * * Contributor(s): * Bobby Holley + * Brian R. Bondy * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -56,11 +57,16 @@ struct BMPFILEHEADER { PRUint32 bihsize; }; -#define BFH_LENGTH 18 // Note: For our purposes, we include bihsize in the BFH + +// The length of the bitmap file header as defined in the BMP spec. +#define BFH_LENGTH 14 +// Internally we store the bitmap file header with an additional 4 bytes which +// is used to store the bitmap information header size. +#define BFH_INTERNAL_LENGTH 18 #define OS2_BIH_LENGTH 12 // This is the real BIH size (as contained in the bihsize field of BMPFILEHEADER) -#define OS2_HEADER_LENGTH (BFH_LENGTH + 8) -#define WIN_HEADER_LENGTH (BFH_LENGTH + 36) +#define OS2_HEADER_LENGTH (BFH_INTERNAL_LENGTH + 8) +#define WIN_HEADER_LENGTH (BFH_INTERNAL_LENGTH + 36) struct BMPINFOHEADER { PRInt32 width; // Uint16 in OS/2 BMPs @@ -106,17 +112,34 @@ struct bitFields { (((((PRUint32) x) >> 8) & 0xFF) << 16) | \ (((((PRUint32) x) >> 16) & 0xFF) << 8) | \ (((PRUint32) x) >> 24)) + +#define NATIVE32_TO_LITTLE LITTLE_TO_NATIVE32 + #else #define LITTLE_TO_NATIVE16(x) x #define LITTLE_TO_NATIVE32(x) x +#define NATIVE32_TO_LITTLE(x) x + #endif #define USE_RGB // BMPINFOHEADER.compression defines +#ifndef BI_RGB +#define BI_RGB 0 +#endif +#ifndef BI_RLE8 #define BI_RLE8 1 +#endif +#ifndef BI_RLE4 #define BI_RLE4 2 +#endif +#ifndef BI_BITFIELDS #define BI_BITFIELDS 3 +#endif +// BI_ALPHABITFIELDS means no compression and specifies alpha bits +// valid only for 32bpp and 16bpp +#define BI_ALPHABITFIELDS 4 // RLE Escape codes #define RLE_ESCAPE 0 @@ -146,6 +169,21 @@ public: nsBMPDecoder(); ~nsBMPDecoder(); + // Specifies whether or not the BMP file will contain alpha data + // If set to true and the BMP is 32BPP, the alpha data will be + // retrieved from the 4th byte of image data per pixel + void SetUseAlphaData(PRBool useAlphaData); + // Obtains the bits per pixel from the internal BIH header + PRInt32 GetBitsPerPixel() const; + // Obtains the width from the internal BIH header + PRInt32 GetWidth() const; + // Obtains the height from the internal BIH header + PRInt32 GetHeight() const; + // Obtains the internal output image buffer + PRUint32* GetImageData(); + // Obtains the size of the compressed image resource + PRInt32 GetCompressedImageSize() const; + virtual void WriteInternal(const char* aBuffer, PRUint32 aCount); virtual void FinishInternal(); @@ -184,6 +222,16 @@ private: /** Set mBIH from the raw data in mRawBuf, converting from little-endian * data to native data as necessary */ void ProcessInfoHeader(); + + // Stores whether the image data stores alpha data, or if + // the alpha data is unspecified and filled with a + // padding byte of 0. + // When a 32BPP bitmap is stored in an ICO or CUR file, its 4th byte + // is used for alpha transparency. When it is stored in a BMP, its + // 4th byte is reserved and is always 0. + // Reference: + // http://en.wikipedia.org/wiki/ICO_(file_format)#cite_note-9 + PRPackedBool mUseAlphaData; }; /** Sets the pixel data in aDecoded to the given values. diff --git a/modules/libpr0n/decoders/nsICODecoder.cpp b/modules/libpr0n/decoders/nsICODecoder.cpp index 43ad195cd6b..e217eeb2b35 100644 --- a/modules/libpr0n/decoders/nsICODecoder.cpp +++ b/modules/libpr0n/decoders/nsICODecoder.cpp @@ -23,6 +23,7 @@ * David Hyatt (Original Author) * Christian Biesinger * Bobby Holley + * Brian R. Bondy * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -65,39 +66,51 @@ namespace imagelib { // Actual Data Processing // ---------------------------------------- -PRUint32 nsICODecoder::CalcAlphaRowSize() +PRUint32 +nsICODecoder::CalcAlphaRowSize() { // Calculate rowsize in DWORD's and then return in # of bytes PRUint32 rowSize = (mDirEntry.mWidth + 31) / 32; // +31 to round up - return rowSize * 4; // Return rowSize in bytes + return rowSize * 4; // Return rowSize in bytes } +// Obtains the number of colors from the bits per pixel +PRUint16 +nsICODecoder::GetNumColors() +{ + PRUint16 numColors = 0; + if (mBPP <= 8) { + switch (mBPP) { + case 1: + numColors = 2; + break; + case 4: + numColors = 16; + break; + case 8: + numColors = 256; + break; + default: + numColors = (PRUint16)-1; + } + } + return numColors; +} + + nsICODecoder::nsICODecoder() { - mPos = mNumColors = mRowBytes = mImageOffset = mCurrIcon = mNumIcons = 0; - mCurLine = 1; // Otherwise decoder will never start - mColors = nsnull; + mPos = mImageOffset = mCurrIcon = mNumIcons = mBPP = mRowBytes = 0; + mIsPNG = PR_FALSE; mRow = nsnull; - mHaveAlphaData = mDecodingAndMask = PR_FALSE; + mOldLine = mCurLine = 1; // Otherwise decoder will never start } nsICODecoder::~nsICODecoder() { - mPos = 0; - - delete[] mColors; - - mCurLine = 0; - mRowBytes = 0; - mImageOffset = 0; - mCurrIcon = 0; - mNumIcons = 0; - if (mRow) { - free(mRow); - mRow = nsnull; + moz_free(mRow); } - mDecodingAndMask = PR_FALSE; } void @@ -106,18 +119,87 @@ nsICODecoder::FinishInternal() // We shouldn't be called in error cases NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call FinishInternal after error!"); - // We should never make multiple frames - NS_ABORT_IF_FALSE(GetFrameCount() <= 1, "Multiple ICO frames?"); + // Finish the internally used decoder as well + if (mContainedDecoder) { + mContainedDecoder->FinishSharedDecoder(); + mDecodeDone = mContainedDecoder->GetDecodeDone(); + } +} - // Send notifications if appropriate - if (!IsSizeDecode() && (GetFrameCount() == 1)) { +// 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 PR_TRUE if successful +PRBool nsICODecoder::FillBitmapFileHeaderBuffer(PRInt8 *bfh) +{ + memset(bfh, 0, 14); + bfh[0] = 'B'; + bfh[1] = 'M'; + PRInt32 dataOffset = 0; + PRInt32 fileSize = 0; + dataOffset = BFH_LENGTH + BITMAPINFOSIZE; - // Invalidate - nsIntRect r(0, 0, mDirEntry.mWidth, mDirEntry.mHeight); - PostInvalidation(r); + // The color table is present only if BPP is <= 8 + if (mDirEntry.mBitCount <= 8) { + PRUint16 numColors = GetNumColors(); + if (numColors == (PRUint16)-1) { + return PR_FALSE; + } + dataOffset += 4 * numColors; + fileSize = dataOffset + mDirEntry.mWidth * mDirEntry.mHeight; + } else { + fileSize = dataOffset + (mDirEntry.mBitCount * mDirEntry.mWidth * + mDirEntry.mHeight) / 8; + } - PostFrameStop(); - PostDecodeDone(); + fileSize = NATIVE32_TO_LITTLE(fileSize); + memcpy(bfh + 2, &fileSize, sizeof(fileSize)); + dataOffset = NATIVE32_TO_LITTLE(dataOffset); + memcpy(bfh + 10, &dataOffset, sizeof(dataOffset)); + return PR_TRUE; +} + +// A BMP inside of an ICO has *2 height because of the AND mask +// that follows the actual bitmap. The BMP shouldn't know about +// this difference though. +void +nsICODecoder::FillBitmapInformationBufferHeight(PRInt8 *bih) +{ + PRInt32 height = mDirEntry.mHeight; + height = NATIVE32_TO_LITTLE(height); + memcpy(bih + 8, &height, sizeof(height)); +} + +// The BMP information header's bits per pixel should be trusted +// more than what we have. Usually the ICO's BPP is set to 0 +PRInt32 +nsICODecoder::ExtractBPPFromBitmap(PRInt8 *bih) +{ + PRInt32 bitsPerPixel; + memcpy(&bitsPerPixel, bih + 14, sizeof(bitsPerPixel)); + bitsPerPixel = LITTLE_TO_NATIVE32(bitsPerPixel); + return bitsPerPixel; +} + +void +nsICODecoder::SetHotSpotIfCursor() { + if (!mIsCursor) { + return; + } + + nsCOMPtr intwrapx = + do_CreateInstance("@mozilla.org/supports-PRUint32;1"); + nsCOMPtr intwrapy = + do_CreateInstance("@mozilla.org/supports-PRUint32;1"); + + if (intwrapx && intwrapy) { + intwrapx->SetData(mDirEntry.mXHotspot); + intwrapy->SetData(mDirEntry.mYHotspot); + + mImage->Set("hotspotX", intwrapx); + mImage->Set("hotspotY", intwrapy); } } @@ -151,12 +233,15 @@ nsICODecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) return; // Nothing to do. PRUint16 colorDepth = 0; - 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) + // 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; @@ -166,15 +251,18 @@ nsICODecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) return; // Need more data IconDirEntry e; - if (mPos == 22+mCurrIcon*sizeof(mDirEntryArray)) { + if (mPos == (DIRENTRYOFFSET + ICODIRENTRYSIZE) + + (mCurrIcon * sizeof(mDirEntryArray))) { mCurrIcon++; ProcessDirEntry(e); - if ((e.mWidth == PREFICONSIZE && e.mHeight == PREFICONSIZE && e.mBitCount >= colorDepth) - || (mCurrIcon == mNumIcons && mImageOffset == 0)) { + if ((e.mWidth == PREFICONSIZE && e.mHeight == PREFICONSIZE && + e.mBitCount >= colorDepth) || + (mCurrIcon == mNumIcons && mImageOffset == 0)) { mImageOffset = e.mImageOffset; - // ensure mImageOffset is >= the size of the direntry headers (bug #245631) - PRUint32 minImageOffset = DIRENTRYOFFSET + mNumIcons*sizeof(mDirEntryArray); + // ensure mImageOffset is >= size of the direntry headers (bug #245631) + PRUint32 minImageOffset = DIRENTRYOFFSET + + mNumIcons * sizeof(mDirEntryArray); if (mImageOffset < minImageOffset) { PostDataError(); return; @@ -197,7 +285,67 @@ nsICODecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) aCount -= toSkip; } - if (mCurrIcon == mNumIcons && mPos >= mImageOffset && mPos < mImageOffset + BITMAPINFOSIZE) { + // 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(mContainedDecoder.get())->HasValidInfo() && + static_cast(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) @@ -209,257 +357,154 @@ nsICODecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) aBuffer += toCopy; } - nsresult rv; + // If we have a BMP inside the ICO and we have read the BIH header + if (!mIsPNG && mPos == mImageOffset + BITMAPINFOSIZE) { + // 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((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); - if (mPos == mImageOffset + BITMAPINFOSIZE) { - - ProcessInfoHeader(); - PostSize(mDirEntry.mWidth, mDirEntry.mHeight); - if (IsSizeDecode()) - return; - - if (mBIH.bpp <= 8) { - switch (mBIH.bpp) { - case 1: - mNumColors = 2; - break; - case 4: - mNumColors = 16; - break; - case 8: - mNumColors = 256; - break; - default: - PostDataError(); - return; - } - - mColors = new colorTable[mNumColors]; - } - - if (mIsCursor) { - nsCOMPtr intwrapx = do_CreateInstance("@mozilla.org/supports-PRUint32;1"); - nsCOMPtr intwrapy = do_CreateInstance("@mozilla.org/supports-PRUint32;1"); - - if (intwrapx && intwrapy) { - intwrapx->SetData(mDirEntry.mXHotspot); - intwrapy->SetData(mDirEntry.mYHotspot); - - mImage->Set("hotspotX", intwrapx); - mImage->Set("hotspotY", intwrapy); - } - } - - mCurLine = mDirEntry.mHeight; - mRow = (PRUint8*)moz_malloc((mDirEntry.mWidth * mBIH.bpp)/8 + 4); - // +4 because the line is padded to a 4 bit boundary, but I don't want - // to make exact calculations here, that's unnecessary. - // Also, it compensates rounding error. - if (!mRow) { - PostDecoderError(NS_ERROR_OUT_OF_MEMORY); - return; - } - - PRUint32 imageLength; - rv = mImage->EnsureFrame(0, 0, 0, mDirEntry.mWidth, mDirEntry.mHeight, - gfxASurface::ImageFormatARGB32, (PRUint8**)&mImageData, &imageLength); - if (NS_FAILED(rv)) { - PostDecoderError(rv); - return; - } - - // Tell the superclass we're starting a frame - PostFrameStart(); - } - - if (mColors && (mPos >= mImageOffset + BITMAPINFOSIZE) && - (mPos < (mImageOffset + BITMAPINFOSIZE + mNumColors * 4))) { - // We will receive (mNumColors * 4) bytes of color data - PRUint32 colorBytes = mPos - (mImageOffset + 40); // Number of bytes already received - PRUint8 colorNum = colorBytes / 4; // Color which is currently received - PRUint8 at = colorBytes % 4; - while (aCount && (mPos < (mImageOffset + BITMAPINFOSIZE + mNumColors * 4))) { - switch (at) { - case 0: - mColors[colorNum].blue = *aBuffer; - break; - case 1: - mColors[colorNum].green = *aBuffer; - break; - case 2: - mColors[colorNum].red = *aBuffer; - break; - case 3: - colorNum++; // This is a padding byte - break; - } - mPos++; aBuffer++; aCount--; - at = (at + 1) % 4; - } - } - - if (!mDecodingAndMask && (mPos >= (mImageOffset + BITMAPINFOSIZE + mNumColors*4))) { - if (mPos == (mImageOffset + BITMAPINFOSIZE + mNumColors*4)) { - // Increment mPos to avoid reprocessing the info header. - mPos++; - } - - // Ensure memory has been allocated before decoding. If we get this far - // without allocated memory, the file is most likely invalid. - // XXXbholley - If null values can be triggered by bad input, why are we - // asserting here? - NS_ASSERTION(mRow, "mRow is null"); - NS_ASSERTION(mImageData, "mImageData is null"); - if (!mRow || !mImageData) { + // 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; + } - PRUint32 rowSize = (mBIH.bpp * mDirEntry.mWidth + 7) / 8; // +7 to round up - if (rowSize % 4) - rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary - PRUint32 toCopy; - do { - toCopy = rowSize - mRowBytes; - if (toCopy) { - if (toCopy > aCount) - toCopy = aCount; + // 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; + } + + // 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(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(mContainedDecoder.get())->GetBitsPerPixel() != 32) { + PRUint32 rowSize = ((mDirEntry.mWidth + 31) / 32) * 4; // + 31 to round up + if (mPos == bmpDataEnd) { + mPos++; + mRowBytes = 0; + mCurLine = mDirEntry.mHeight; + 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) { + } + if (rowSize == mRowBytes) { mCurLine--; - PRUint32* d = mImageData + (mCurLine * mDirEntry.mWidth); - PRUint8* p = mRow; - PRUint32 lpos = mDirEntry.mWidth; - switch (mBIH.bpp) { - case 1: - while (lpos > 0) { - PRInt8 bit; - PRUint8 idx; - for (bit = 7; bit >= 0 && lpos > 0; bit--) { - idx = (*p >> bit) & 1; - SetPixel(d, idx, mColors); - --lpos; - } - ++p; - } - break; - case 4: - while (lpos > 0) { - Set4BitPixel(d, *p, lpos, mColors); - ++p; - } - break; - case 8: - while (lpos > 0) { - SetPixel(d, *p, mColors); - --lpos; - ++p; - } - break; - case 16: - while (lpos > 0) { - SetPixel(d, - (p[1] & 124) << 1, - ((p[1] & 3) << 6) | ((p[0] & 224) >> 2), - (p[0] & 31) << 3); - - --lpos; - p+=2; - } - break; - case 24: - while (lpos > 0) { - SetPixel(d, p[2], p[1], p[0]); - p += 3; - --lpos; - } - break; - case 32: - // We assume that 32bit doesn't have alpha data until we - // find a non-zero alpha byte. If we find such a byte, - // it means that all previous pixels are really clear (alphabyte=0). - // This working assumption prevents us having to premultiply afterwards. - while (lpos > 0) { - if (!mHaveAlphaData && p[3]) { - // Non-zero alpha byte detected! Clear previous pixels from current row to end - memset(mImageData + mCurLine * mDirEntry.mWidth, 0, - (mDirEntry.mHeight - mCurLine) * mDirEntry.mWidth * sizeof(PRUint32)); - mHaveAlphaData = PR_TRUE; - } - SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF); - p += 4; - --lpos; - } - break; - default: - // This is probably the wrong place to check this... - PostDataError(); - return; - } - - if (mCurLine == 0) - mDecodingAndMask = PR_TRUE; - mRowBytes = 0; - } - } while (!mDecodingAndMask && aCount > 0); - } - - if (mDecodingAndMask && !mHaveAlphaData) { - PRUint32 rowSize = CalcAlphaRowSize(); - - if (mPos == (1 + mImageOffset + BITMAPINFOSIZE + mNumColors*4)) { - mPos++; - mRowBytes = 0; - mCurLine = mDirEntry.mHeight; - mRow = (PRUint8*)realloc(mRow, rowSize); - if (!mRow) { - PostDecoderError(NS_ERROR_OUT_OF_MEMORY); - return; - } - } - - // Ensure memory has been allocated before decoding. - NS_ASSERTION(mRow, "mRow is null"); - NS_ASSERTION(mImageData, "mImageData is null"); - if (!mRow || !mImageData) { - 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* decoded = mImageData + mCurLine * mDirEntry.mWidth; - PRUint32* decoded_end = decoded + mDirEntry.mWidth; - PRUint8* p = mRow, *p_end = mRow + rowSize; - while (p < p_end) { - PRUint8 idx = *p++; - for (PRUint8 bit = 0x80; bit && decoded>= 1) { - // Clear pixel completely for transparency. - if (idx & bit) *decoded = 0; - decoded ++; + PRUint32* imageData = static_cast(mContainedDecoder.get())->GetImageData(); + PRUint32* decoded = imageData + mCurLine * mDirEntry.mWidth; + PRUint32* decoded_end = decoded + mDirEntry.mWidth; + PRUint8* p = mRow, *p_end = mRow + rowSize; + while (p < p_end) { + PRUint8 idx = *p++; + for (PRUint8 bit = 0x80; bit && decoded>= 1) { + // Clear pixel completely for transparency. + if (idx & bit) { + *decoded = 0; + } + decoded++; + } + } } } } } } - - return; } void @@ -467,51 +512,19 @@ nsICODecoder::ProcessDirEntry(IconDirEntry& aTarget) { memset(&aTarget, 0, sizeof(aTarget)); memcpy(&aTarget.mWidth, mDirEntryArray, sizeof(aTarget.mWidth)); - memcpy(&aTarget.mHeight, mDirEntryArray+1, sizeof(aTarget.mHeight)); - memcpy(&aTarget.mColorCount, mDirEntryArray+2, sizeof(aTarget.mColorCount)); - memcpy(&aTarget.mReserved, mDirEntryArray+3, sizeof(aTarget.mReserved)); - - memcpy(&aTarget.mPlanes, mDirEntryArray+4, sizeof(aTarget.mPlanes)); + memcpy(&aTarget.mHeight, mDirEntryArray + 1, sizeof(aTarget.mHeight)); + memcpy(&aTarget.mColorCount, mDirEntryArray + 2, sizeof(aTarget.mColorCount)); + memcpy(&aTarget.mReserved, mDirEntryArray + 3, sizeof(aTarget.mReserved)); + memcpy(&aTarget.mPlanes, mDirEntryArray + 4, sizeof(aTarget.mPlanes)); aTarget.mPlanes = LITTLE_TO_NATIVE16(aTarget.mPlanes); - - memcpy(&aTarget.mBitCount, mDirEntryArray+6, sizeof(aTarget.mBitCount)); + memcpy(&aTarget.mBitCount, mDirEntryArray + 6, sizeof(aTarget.mBitCount)); aTarget.mBitCount = LITTLE_TO_NATIVE16(aTarget.mBitCount); - - memcpy(&aTarget.mBytesInRes, mDirEntryArray+8, sizeof(aTarget.mBytesInRes)); + memcpy(&aTarget.mBytesInRes, mDirEntryArray + 8, sizeof(aTarget.mBytesInRes)); aTarget.mBytesInRes = LITTLE_TO_NATIVE32(aTarget.mBytesInRes); - - memcpy(&aTarget.mImageOffset, mDirEntryArray+12, sizeof(aTarget.mImageOffset)); + memcpy(&aTarget.mImageOffset, mDirEntryArray + 12, + sizeof(aTarget.mImageOffset)); aTarget.mImageOffset = LITTLE_TO_NATIVE32(aTarget.mImageOffset); } -void nsICODecoder::ProcessInfoHeader() { - memset(&mBIH, 0, sizeof(mBIH)); - // Ignoring the size; it should always be 40 for icons, anyway - - memcpy(&mBIH.width, mBIHraw + 4, sizeof(mBIH.width)); - memcpy(&mBIH.height, mBIHraw + 8, sizeof(mBIH.height)); - memcpy(&mBIH.planes, mBIHraw + 12, sizeof(mBIH.planes)); - memcpy(&mBIH.bpp, mBIHraw + 14, sizeof(mBIH.bpp)); - memcpy(&mBIH.compression, mBIHraw + 16, sizeof(mBIH.compression)); - memcpy(&mBIH.image_size, mBIHraw + 20, sizeof(mBIH.image_size)); - memcpy(&mBIH.xppm, mBIHraw + 24, sizeof(mBIH.xppm)); - memcpy(&mBIH.yppm, mBIHraw + 28, sizeof(mBIH.yppm)); - memcpy(&mBIH.colors, mBIHraw + 32, sizeof(mBIH.colors)); - memcpy(&mBIH.important_colors, mBIHraw + 36, sizeof(mBIH.important_colors)); - - // Convert endianness - mBIH.width = LITTLE_TO_NATIVE32(mBIH.width); - mBIH.height = LITTLE_TO_NATIVE32(mBIH.height); - mBIH.planes = LITTLE_TO_NATIVE16(mBIH.planes); - mBIH.bpp = LITTLE_TO_NATIVE16(mBIH.bpp); - - mBIH.compression = LITTLE_TO_NATIVE32(mBIH.compression); - mBIH.image_size = LITTLE_TO_NATIVE32(mBIH.image_size); - mBIH.xppm = LITTLE_TO_NATIVE32(mBIH.xppm); - mBIH.yppm = LITTLE_TO_NATIVE32(mBIH.yppm); - mBIH.colors = LITTLE_TO_NATIVE32(mBIH.colors); - mBIH.important_colors = LITTLE_TO_NATIVE32(mBIH.important_colors); -} - } // namespace imagelib } // namespace mozilla diff --git a/modules/libpr0n/decoders/nsICODecoder.h b/modules/libpr0n/decoders/nsICODecoder.h index 2b463857eaa..9b2188bc16e 100644 --- a/modules/libpr0n/decoders/nsICODecoder.h +++ b/modules/libpr0n/decoders/nsICODecoder.h @@ -22,6 +22,7 @@ * Contributor(s): * David Hyatt (Original Author) * Bobby Holley + * Brian R. Bondy * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -45,10 +46,15 @@ #include "Decoder.h" #include "imgIDecoderObserver.h" #include "nsBMPDecoder.h" +#include "nsPNGDecoder.h" namespace mozilla { namespace imagelib { +#define ICODIRENTRYSIZE 16 +#define PNGSIGNATURESIZE 8 +#define BMPFILEHEADERSIZE 14 + class RasterImage; struct IconDirEntry @@ -80,37 +86,42 @@ public: virtual void FinishInternal(); private: - // Private helper methods + // Processes a single dir entry of the icon resource void ProcessDirEntry(IconDirEntry& aTarget); - void ProcessInfoHeader(); - - nsresult SetImageData(); - + // Sets the hotspot property of if we have a cursor + void SetHotSpotIfCursor(); + // Creates a bitmap file header buffer, returns PR_TRUE if successful + PRBool FillBitmapFileHeaderBuffer(PRInt8 *bfh); + // Fixes the height of a BMP information header field + void FillBitmapInformationBufferHeight(PRInt8 *bih); + // Extract bit count from BMP information header + PRInt32 ExtractBPPFromBitmap(PRInt8 *bih); + // Calculates the row size in bytes for the AND mask table PRUint32 CalcAlphaRowSize(); + // Obtains the number of colors from the BPP, mBPP must be filled in + PRUint16 GetNumColors(); - PRUint32 mPos; - PRUint16 mNumIcons; - PRUint16 mCurrIcon; - PRUint32 mImageOffset; - - char mDirEntryArray[16]; - IconDirEntry mDirEntry; - - char mBIHraw[40]; - BMPINFOHEADER mBIH; - - PRUint32 mNumColors; - colorTable* mColors; - - PRUint8* mRow; // Holds one raw line of the image + PRUint16 mBPP; // Stores the images BPP + PRUint32 mPos; // Keeps track of the position we have decoded up until + PRUint16 mNumIcons; // Stores the number of icons in the ICO file + PRUint16 mCurrIcon; // Stores the current dir entry index we are processing + PRUint32 mImageOffset; // Stores the offset of the image data we want + PRUint8 *mRow; // Holds one raw line of the image + PRInt32 mCurLine; // Line index of the image that's currently being decoded PRUint32 mRowBytes; // How many bytes of the row were already received - PRInt32 mCurLine; + PRInt32 mOldLine; // Previous index of the line + nsAutoPtr mContainedDecoder; // Contains either a BMP or PNG resource - PRUint32* mImageData; - - PRPackedBool mHaveAlphaData; + char mDirEntryArray[ICODIRENTRYSIZE]; // Holds the current dir entry buffer + IconDirEntry mDirEntry; // Holds a decoded dir entry + // Holds the potential bytes that can be a PNG signature + char mSignature[PNGSIGNATURESIZE]; + // Holds the potential bytes for a bitmap information header + char mBIHraw[40]; + // Stores whether or not the icon file we are processing has type 1 (icon) PRPackedBool mIsCursor; - PRPackedBool mDecodingAndMask; + // Stores whether or not the contained resource is a PNG + PRPackedBool mIsPNG; }; } // namespace imagelib diff --git a/modules/libpr0n/decoders/nsPNGDecoder.cpp b/modules/libpr0n/decoders/nsPNGDecoder.cpp index 201d0cbe4c4..f6e67cae613 100644 --- a/modules/libpr0n/decoders/nsPNGDecoder.cpp +++ b/modules/libpr0n/decoders/nsPNGDecoder.cpp @@ -77,10 +77,9 @@ static PRLogModuleInfo *gPNGDecoderAccountingLog = #define HEIGHT_OFFSET (WIDTH_OFFSET + 4) #define BYTES_NEEDED_FOR_DIMENSIONS (HEIGHT_OFFSET + 4) -// This is defined in the PNG spec as an invariant. We use it to -// do manual validation without libpng. -static const PRUint8 pngSignatureBytes[] = - { 137, 80, 78, 71, 13, 10, 26, 10 }; +// First 8 bytes of a PNG file +const PRUint8 +nsPNGDecoder::pngSignatureBytes[] = { 137, 80, 78, 71, 13, 10, 26, 10 }; nsPNGDecoder::nsPNGDecoder() : mPNG(nsnull), mInfo(nsnull), @@ -317,7 +316,8 @@ nsPNGDecoder::WriteInternal(const char *aBuffer, PRUint32 aCount) if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS) { // Check that the signature bytes are right - if (memcmp(mHeaderBuf, pngSignatureBytes, sizeof(pngSignatureBytes))) { + if (memcmp(mHeaderBuf, nsPNGDecoder::pngSignatureBytes, + sizeof(pngSignatureBytes))) { PostDataError(); return; } diff --git a/modules/libpr0n/decoders/nsPNGDecoder.h b/modules/libpr0n/decoders/nsPNGDecoder.h index a214a9acaad..39bba8fe92c 100644 --- a/modules/libpr0n/decoders/nsPNGDecoder.h +++ b/modules/libpr0n/decoders/nsPNGDecoder.h @@ -72,6 +72,21 @@ public: void EndImageFrame(); + // Checks if the info header contains valid information + bool HasValidInfo() const + { + return mInfo && mInfo->valid; + } + + // Obtain the pixel depth if available or 0 otherwise + PRInt32 GetPixelDepth() const + { + if (!mInfo) { + return 0; + } + return mInfo->pixel_depth; + } + public: png_structp mPNG; png_infop mInfo; @@ -113,6 +128,10 @@ public: png_const_charp error_msg); static void PNGAPI warning_callback(png_structp png_ptr, png_const_charp warning_msg); + + // This is defined in the PNG spec as an invariant. We use it to + // do manual validation without libpng. + static const PRUint8 pngSignatureBytes[]; }; } // namespace imagelib diff --git a/modules/libpr0n/src/Decoder.cpp b/modules/libpr0n/src/Decoder.cpp index 97087621b0c..667a9ee8422 100644 --- a/modules/libpr0n/src/Decoder.cpp +++ b/modules/libpr0n/src/Decoder.cpp @@ -88,6 +88,26 @@ Decoder::Init(RasterImage* aImage, imgIDecoderObserver* aObserver) mInitialized = true; } +// Initializes a decoder whose aImage and aObserver is already being used by a +// parent decoder +void +Decoder::InitSharedDecoder(RasterImage* aImage, imgIDecoderObserver* aObserver) +{ + // We should always have an image + NS_ABORT_IF_FALSE(aImage, "Can't initialize decoder without an image!"); + + // No re-initializing + NS_ABORT_IF_FALSE(mImage == nsnull, "Can't re-initialize a decoder!"); + + // Save our parameters + mImage = aImage; + mObserver = aObserver; + + // Implementation-specific initialization + InitInternal(); + mInitialized = true; +} + void Decoder::Write(const char* aBuffer, PRUint32 aCount) { @@ -155,6 +175,14 @@ Decoder::Finish() } } +void +Decoder::FinishSharedDecoder() +{ + if (!HasError()) { + FinishInternal(); + } +} + void Decoder::FlushInvalidations() { diff --git a/modules/libpr0n/src/Decoder.h b/modules/libpr0n/src/Decoder.h index e2e166bddfb..f42c98c61f6 100644 --- a/modules/libpr0n/src/Decoder.h +++ b/modules/libpr0n/src/Decoder.h @@ -63,6 +63,18 @@ public: */ void Init(RasterImage* aImage, imgIDecoderObserver* aObserver); + + /** + * Initializes a decoder whose aImage and aObserver is already being used by a + * parent decoder. Decoders may not be re-initialized. + * + * @param aContainer The image container to decode to. + * @param aObserver The observer for decode notification events. + * + * Notifications Sent: TODO + */ + void InitSharedDecoder(RasterImage* aImage, imgIDecoderObserver* aObserver); + /** * Writes data to the decoder. * @@ -82,6 +94,14 @@ public: */ void Finish(); + /** + * Informs the shared decoder that all the data has been written. + * Should only be used if InitSharedDecoder was useed + * + * Notifications Sent: TODO + */ + void FinishSharedDecoder(); + /** * Tells the decoder to flush any pending invalidations. This informs the image * frame of its decoded region, and sends the appropriate OnDataAvailable call @@ -122,6 +142,9 @@ public: bool HasDecoderError() { return NS_FAILED(mFailCode); }; nsresult GetDecoderError() { return mFailCode; }; void PostResizeError() { PostDataError(); } + bool GetDecodeDone() const { + return mDecodeDone; + } // flags. Keep these in sync with imgIContainer.idl. // SetDecodeFlags must be called before Init(), otherwise @@ -176,12 +199,13 @@ protected: * */ nsRefPtr mImage; - - PRUint32 mDecodeFlags; - -private: nsCOMPtr mObserver; + PRUint32 mDecodeFlags; + bool mDecodeDone; + bool mDataError; + +private: PRUint32 mFrameCount; // Number of frames, including anything in-progress nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame. @@ -191,8 +215,6 @@ private: bool mInitialized; bool mSizeDecode; bool mInFrame; - bool mDecodeDone; - bool mDataError; }; } // namespace imagelib