Bug 600556 - Support Vista-style PNG ICO files. r=joe

This commit is contained in:
Brian R. Bondy 2011-08-25 16:09:01 -04:00
Родитель c6dbea4d8c
Коммит e8392a91ce
8 изменённых файлов: 643 добавлений и 388 удалений

Просмотреть файл

@ -22,6 +22,7 @@
* Contributor(s): * Contributor(s):
* Neil Rashbrook <neil@parkwaycc.co.uk> * Neil Rashbrook <neil@parkwaycc.co.uk>
* Bobby Holley <bobbyholley@gmail.com> * Bobby Holley <bobbyholley@gmail.com>
* Brian R. Bondy <netzen@gmail.com>
* *
* Alternatively, the contents of this file may be used under the terms of * 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 * either the GNU General Public License Version 2 or later (the "GPL"), or
@ -70,13 +71,73 @@ nsBMPDecoder::nsBMPDecoder()
mState = eRLEStateInitial; mState = eRLEStateInitial;
mStateData = 0; mStateData = 0;
mLOH = WIN_HEADER_LENGTH; mLOH = WIN_HEADER_LENGTH;
mUseAlphaData = PR_FALSE;
} }
nsBMPDecoder::~nsBMPDecoder() nsBMPDecoder::~nsBMPDecoder()
{ {
delete[] mColors; delete[] mColors;
if (mRow) if (mRow) {
free(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 void
@ -90,6 +151,11 @@ nsBMPDecoder::FinishInternal()
// Send notifications if appropriate // Send notifications if appropriate
if (!IsSizeDecode() && (GetFrameCount() == 1)) { if (!IsSizeDecode() && (GetFrameCount() == 1)) {
// Invalidate
nsIntRect r(0, 0, mBIH.width, mBIH.height);
PostInvalidation(r);
PostFrameStop(); PostFrameStop();
PostDecodeDone(); PostDecodeDone();
} }
@ -145,8 +211,8 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
return; return;
nsresult rv; nsresult rv;
if (mPos < BFH_LENGTH) { /* In BITMAPFILEHEADER */ if (mPos < BFH_INTERNAL_LENGTH) { /* In BITMAPFILEHEADER */
PRUint32 toCopy = BFH_LENGTH - mPos; PRUint32 toCopy = BFH_INTERNAL_LENGTH - mPos;
if (toCopy > aCount) if (toCopy > aCount)
toCopy = aCount; toCopy = aCount;
memcpy(mRawBuf + mPos, aBuffer, toCopy); memcpy(mRawBuf + mPos, aBuffer, toCopy);
@ -154,7 +220,7 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
aCount -= toCopy; aCount -= toCopy;
aBuffer += toCopy; aBuffer += toCopy;
} }
if (mPos == BFH_LENGTH) { if (mPos == BFH_INTERNAL_LENGTH) {
ProcessFileHeader(); ProcessFileHeader();
if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M') { if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M') {
PostDataError(); PostDataError();
@ -163,19 +229,23 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
if (mBFH.bihsize == OS2_BIH_LENGTH) if (mBFH.bihsize == OS2_BIH_LENGTH)
mLOH = OS2_HEADER_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; PRUint32 toCopy = mLOH - mPos;
if (toCopy > aCount) if (toCopy > aCount)
toCopy = aCount; toCopy = aCount;
memcpy(mRawBuf + (mPos - BFH_LENGTH), aBuffer, toCopy); memcpy(mRawBuf + (mPos - BFH_INTERNAL_LENGTH), aBuffer, toCopy);
mPos += toCopy; mPos += toCopy;
aCount -= toCopy; aCount -= toCopy;
aBuffer += 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(); ProcessInfoHeader();
PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP image is %lix%lix%lu. compression=%lu\n", PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP is %lix%lix%lu. compression=%lu\n",
mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression)); mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression));
// Verify we support this bit depth // Verify we support this bit depth
if (mBIH.bpp != 1 && mBIH.bpp != 4 && mBIH.bpp != 8 && if (mBIH.bpp != 1 && mBIH.bpp != 4 && mBIH.bpp != 8 &&
mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32) { mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32) {
@ -226,36 +296,70 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
CalcBitShift(); 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; PRUint32 imageLength;
if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) { if (mBIH.compression == BI_RLE8 || mBIH.compression == BI_RLE4 ||
rv = mImage->EnsureFrame(0, 0, 0, mBIH.width, real_height, gfxASurface::ImageFormatARGB32, mBIH.compression == BI_ALPHABITFIELDS) {
rv = mImage->EnsureFrame(0, 0, 0, mBIH.width, real_height,
gfxASurface::ImageFormatARGB32,
(PRUint8**)&mImageData, &imageLength); (PRUint8**)&mImageData, &imageLength);
} else { } else {
// mRow is not used for RLE encoded images // mRow is not used for RLE encoded images
mRow = (PRUint8*)moz_malloc((mBIH.width * mBIH.bpp)/8 + 4); 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 // + 4 because the line is padded to a 4 bit boundary, but I don't want
// to make exact calculations here, that's unnecessary. // to make exact calculations here, that's unnecessary.
// Also, it compensates rounding error. // Also, it compensates rounding error.
if (!mRow) { if (!mRow) {
PostDecoderError(NS_ERROR_OUT_OF_MEMORY); PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
return; 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) { if (NS_FAILED(rv) || !mImageData) {
PostDecoderError(NS_ERROR_FAILURE); PostDecoderError(NS_ERROR_FAILURE);
return; return;
} }
// Prepare for transparancy // Prepare for transparency
if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) { 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 // Clear the image, as the RLE may jump over areas
memset(mImageData, 0, imageLength); 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 // Need to increment mPos, else we might get to mPos==mLOH again
// From now on, mPos is irrelevant // From now on, mPos is irrelevant
if (!mBIH.compression || mBIH.compression == BI_BITFIELDS) { if (!mBIH.compression || mBIH.compression == BI_BITFIELDS) {
PRUint32 rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // +7 to round up PRUint32 rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up
if (rowSize % 4) if (rowSize % 4) {
rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary
}
PRUint32 toCopy; PRUint32 toCopy;
do { do {
toCopy = rowSize - mRowBytes; toCopy = rowSize - mRowBytes;
@ -370,35 +475,44 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
p+=2; p+=2;
} }
break; break;
case 32:
case 24: case 24:
while (lpos > 0) { while (lpos > 0) {
SetPixel(d, p[2], p[1], p[0]); SetPixel(d, p[2], p[1], p[0]);
p += 2; p += 2;
--lpos; --lpos;
if (mBIH.bpp == 32)
p++; // Padding byte
++p; ++p;
} }
break; 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: default:
NS_NOTREACHED("Unsupported color depth, but earlier check didn't catch it"); NS_NOTREACHED("Unsupported color depth, but earlier check didn't catch it");
} }
mCurLine --; mCurLine --;
if (mCurLine == 0) { // Finished last line if (mCurLine == 0) { // Finished last line
break; break;
} }
mRowBytes = 0; mRowBytes = 0;
} }
} while (aCount > 0); } while (aCount > 0);
} }
else if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) { else if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) ||
|| ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) { ((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")); PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n"));
PostDataError(); PostDataError();
return; return;
} }
while (aCount > 0) { while (aCount > 0) {
@ -562,7 +676,7 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
} }
} }
} }
const PRUint32 rows = mOldLine - mCurLine; const PRUint32 rows = mOldLine - mCurLine;
if (rows) { if (rows) {

Просмотреть файл

@ -21,6 +21,7 @@
* *
* Contributor(s): * Contributor(s):
* Bobby Holley <bobbyholley@gmail.com> * Bobby Holley <bobbyholley@gmail.com>
* Brian R. Bondy <netzen@gmail.com>
* *
* Alternatively, the contents of this file may be used under the terms of * 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 * either the GNU General Public License Version 2 or later (the "GPL"), or
@ -56,11 +57,16 @@ struct BMPFILEHEADER {
PRUint32 bihsize; 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_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 OS2_HEADER_LENGTH (BFH_INTERNAL_LENGTH + 8)
#define WIN_HEADER_LENGTH (BFH_LENGTH + 36) #define WIN_HEADER_LENGTH (BFH_INTERNAL_LENGTH + 36)
struct BMPINFOHEADER { struct BMPINFOHEADER {
PRInt32 width; // Uint16 in OS/2 BMPs PRInt32 width; // Uint16 in OS/2 BMPs
@ -106,17 +112,34 @@ struct bitFields {
(((((PRUint32) x) >> 8) & 0xFF) << 16) | \ (((((PRUint32) x) >> 8) & 0xFF) << 16) | \
(((((PRUint32) x) >> 16) & 0xFF) << 8) | \ (((((PRUint32) x) >> 16) & 0xFF) << 8) | \
(((PRUint32) x) >> 24)) (((PRUint32) x) >> 24))
#define NATIVE32_TO_LITTLE LITTLE_TO_NATIVE32
#else #else
#define LITTLE_TO_NATIVE16(x) x #define LITTLE_TO_NATIVE16(x) x
#define LITTLE_TO_NATIVE32(x) x #define LITTLE_TO_NATIVE32(x) x
#define NATIVE32_TO_LITTLE(x) x
#endif #endif
#define USE_RGB #define USE_RGB
// BMPINFOHEADER.compression defines // BMPINFOHEADER.compression defines
#ifndef BI_RGB
#define BI_RGB 0
#endif
#ifndef BI_RLE8
#define BI_RLE8 1 #define BI_RLE8 1
#endif
#ifndef BI_RLE4
#define BI_RLE4 2 #define BI_RLE4 2
#endif
#ifndef BI_BITFIELDS
#define BI_BITFIELDS 3 #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 // RLE Escape codes
#define RLE_ESCAPE 0 #define RLE_ESCAPE 0
@ -146,6 +169,21 @@ public:
nsBMPDecoder(); nsBMPDecoder();
~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 WriteInternal(const char* aBuffer, PRUint32 aCount);
virtual void FinishInternal(); virtual void FinishInternal();
@ -184,6 +222,16 @@ private:
/** Set mBIH from the raw data in mRawBuf, converting from little-endian /** Set mBIH from the raw data in mRawBuf, converting from little-endian
* data to native data as necessary */ * data to native data as necessary */
void ProcessInfoHeader(); 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. /** Sets the pixel data in aDecoded to the given values.

Просмотреть файл

@ -23,6 +23,7 @@
* David Hyatt <hyatt@netscape.com> (Original Author) * David Hyatt <hyatt@netscape.com> (Original Author)
* Christian Biesinger <cbiesinger@web.de> * Christian Biesinger <cbiesinger@web.de>
* Bobby Holley <bobbyholley@gmail.com> * Bobby Holley <bobbyholley@gmail.com>
* Brian R. Bondy <netzen@gmail.com>
* *
* Alternatively, the contents of this file may be used under the terms of * 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 * either the GNU General Public License Version 2 or later (the "GPL"), or
@ -65,39 +66,51 @@ namespace imagelib {
// Actual Data Processing // Actual Data Processing
// ---------------------------------------- // ----------------------------------------
PRUint32 nsICODecoder::CalcAlphaRowSize() PRUint32
nsICODecoder::CalcAlphaRowSize()
{ {
// Calculate rowsize in DWORD's and then return in # of bytes // Calculate rowsize in DWORD's and then return in # of bytes
PRUint32 rowSize = (mDirEntry.mWidth + 31) / 32; // +31 to round up 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() nsICODecoder::nsICODecoder()
{ {
mPos = mNumColors = mRowBytes = mImageOffset = mCurrIcon = mNumIcons = 0; mPos = mImageOffset = mCurrIcon = mNumIcons = mBPP = mRowBytes = 0;
mCurLine = 1; // Otherwise decoder will never start mIsPNG = PR_FALSE;
mColors = nsnull;
mRow = nsnull; mRow = nsnull;
mHaveAlphaData = mDecodingAndMask = PR_FALSE; mOldLine = mCurLine = 1; // Otherwise decoder will never start
} }
nsICODecoder::~nsICODecoder() nsICODecoder::~nsICODecoder()
{ {
mPos = 0;
delete[] mColors;
mCurLine = 0;
mRowBytes = 0;
mImageOffset = 0;
mCurrIcon = 0;
mNumIcons = 0;
if (mRow) { if (mRow) {
free(mRow); moz_free(mRow);
mRow = nsnull;
} }
mDecodingAndMask = PR_FALSE;
} }
void void
@ -106,18 +119,87 @@ nsICODecoder::FinishInternal()
// We shouldn't be called in error cases // We shouldn't be called in error cases
NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call FinishInternal after error!"); NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call FinishInternal after error!");
// We should never make multiple frames // Finish the internally used decoder as well
NS_ABORT_IF_FALSE(GetFrameCount() <= 1, "Multiple ICO frames?"); if (mContainedDecoder) {
mContainedDecoder->FinishSharedDecoder();
mDecodeDone = mContainedDecoder->GetDecodeDone();
}
}
// Send notifications if appropriate // Returns a buffer filled with the bitmap file header in little endian:
if (!IsSizeDecode() && (GetFrameCount() == 1)) { // 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 // The color table is present only if BPP is <= 8
nsIntRect r(0, 0, mDirEntry.mWidth, mDirEntry.mHeight); if (mDirEntry.mBitCount <= 8) {
PostInvalidation(r); 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(); fileSize = NATIVE32_TO_LITTLE(fileSize);
PostDecodeDone(); 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<nsISupportsPRUint32> intwrapx =
do_CreateInstance("@mozilla.org/supports-PRUint32;1");
nsCOMPtr<nsISupportsPRUint32> 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. return; // Nothing to do.
PRUint16 colorDepth = 0; PRUint16 colorDepth = 0;
while (mCurrIcon < mNumIcons) { // Loop through each entry's dir entry
if (mPos >= DIRENTRYOFFSET + (mCurrIcon*sizeof(mDirEntryArray)) && while (mCurrIcon < mNumIcons) {
mPos < DIRENTRYOFFSET + ((mCurrIcon+1)*sizeof(mDirEntryArray))) { if (mPos >= DIRENTRYOFFSET + (mCurrIcon * sizeof(mDirEntryArray)) &&
PRUint32 toCopy = sizeof(mDirEntryArray) - (mPos - DIRENTRYOFFSET - mCurrIcon*sizeof(mDirEntryArray)); mPos < DIRENTRYOFFSET + ((mCurrIcon + 1) * sizeof(mDirEntryArray))) {
if (toCopy > aCount) PRUint32 toCopy = sizeof(mDirEntryArray) -
(mPos - DIRENTRYOFFSET - mCurrIcon * sizeof(mDirEntryArray));
if (toCopy > aCount) {
toCopy = aCount; toCopy = aCount;
}
memcpy(mDirEntryArray + sizeof(mDirEntryArray) - toCopy, aBuffer, toCopy); memcpy(mDirEntryArray + sizeof(mDirEntryArray) - toCopy, aBuffer, toCopy);
mPos += toCopy; mPos += toCopy;
aCount -= toCopy; aCount -= toCopy;
@ -166,15 +251,18 @@ nsICODecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
return; // Need more data return; // Need more data
IconDirEntry e; IconDirEntry e;
if (mPos == 22+mCurrIcon*sizeof(mDirEntryArray)) { if (mPos == (DIRENTRYOFFSET + ICODIRENTRYSIZE) +
(mCurrIcon * sizeof(mDirEntryArray))) {
mCurrIcon++; mCurrIcon++;
ProcessDirEntry(e); ProcessDirEntry(e);
if ((e.mWidth == PREFICONSIZE && e.mHeight == PREFICONSIZE && e.mBitCount >= colorDepth) if ((e.mWidth == PREFICONSIZE && e.mHeight == PREFICONSIZE &&
|| (mCurrIcon == mNumIcons && mImageOffset == 0)) { e.mBitCount >= colorDepth) ||
(mCurrIcon == mNumIcons && mImageOffset == 0)) {
mImageOffset = e.mImageOffset; mImageOffset = e.mImageOffset;
// ensure mImageOffset is >= the size of the direntry headers (bug #245631) // ensure mImageOffset is >= size of the direntry headers (bug #245631)
PRUint32 minImageOffset = DIRENTRYOFFSET + mNumIcons*sizeof(mDirEntryArray); PRUint32 minImageOffset = DIRENTRYOFFSET +
mNumIcons * sizeof(mDirEntryArray);
if (mImageOffset < minImageOffset) { if (mImageOffset < minImageOffset) {
PostDataError(); PostDataError();
return; return;
@ -197,7 +285,67 @@ nsICODecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
aCount -= toSkip; 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<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. // We've found the icon.
PRUint32 toCopy = sizeof(mBIHraw) - (mPos - mImageOffset); PRUint32 toCopy = sizeof(mBIHraw) - (mPos - mImageOffset);
if (toCopy > aCount) if (toCopy > aCount)
@ -209,257 +357,154 @@ nsICODecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
aBuffer += toCopy; 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) { // 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
ProcessInfoHeader(); // generate this header ourselves and feed it to the BMP decoder.
PostSize(mDirEntry.mWidth, mDirEntry.mHeight); PRInt8 bfhBuffer[BMPFILEHEADERSIZE];
if (IsSizeDecode()) if (!FillBitmapFileHeaderBuffer(bfhBuffer)) {
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<nsISupportsPRUint32> intwrapx = do_CreateInstance("@mozilla.org/supports-PRUint32;1");
nsCOMPtr<nsISupportsPRUint32> 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) {
PostDataError(); PostDataError();
return; 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 // Setup the cursor hot spot if one is present
if (rowSize % 4) SetHotSpotIfCursor();
rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary
PRUint32 toCopy; // Fix the height on the BMP resource
do { FillBitmapInformationBufferHeight((PRInt8*)mBIHraw);
toCopy = rowSize - mRowBytes;
if (toCopy) { // Write out the BMP's bitmap info header
if (toCopy > aCount) mContainedDecoder->Write(mBIHraw, sizeof(mBIHraw));
toCopy = aCount; 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<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) {
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); memcpy(mRow + mRowBytes, aBuffer, toCopy);
aCount -= toCopy; aCount -= toCopy;
aBuffer += toCopy; aBuffer += toCopy;
mRowBytes += toCopy; mRowBytes += toCopy;
} }
if (rowSize == mRowBytes) { if (rowSize == mRowBytes) {
mCurLine--; 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; mRowBytes = 0;
}
} while (!mDecodingAndMask && aCount > 0);
} PRUint32* imageData = static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetImageData();
PRUint32* decoded = imageData + mCurLine * mDirEntry.mWidth;
if (mDecodingAndMask && !mHaveAlphaData) { PRUint32* decoded_end = decoded + mDirEntry.mWidth;
PRUint32 rowSize = CalcAlphaRowSize(); PRUint8* p = mRow, *p_end = mRow + rowSize;
while (p < p_end) {
if (mPos == (1 + mImageOffset + BITMAPINFOSIZE + mNumColors*4)) { PRUint8 idx = *p++;
mPos++; for (PRUint8 bit = 0x80; bit && decoded<decoded_end; bit >>= 1) {
mRowBytes = 0; // Clear pixel completely for transparency.
mCurLine = mDirEntry.mHeight; if (idx & bit) {
mRow = (PRUint8*)realloc(mRow, rowSize); *decoded = 0;
if (!mRow) { }
PostDecoderError(NS_ERROR_OUT_OF_MEMORY); decoded++;
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<decoded_end; bit >>= 1) {
// Clear pixel completely for transparency.
if (idx & bit) *decoded = 0;
decoded ++;
} }
} }
} }
} }
} }
return;
} }
void void
@ -467,51 +512,19 @@ nsICODecoder::ProcessDirEntry(IconDirEntry& aTarget)
{ {
memset(&aTarget, 0, sizeof(aTarget)); memset(&aTarget, 0, sizeof(aTarget));
memcpy(&aTarget.mWidth, mDirEntryArray, sizeof(aTarget.mWidth)); memcpy(&aTarget.mWidth, mDirEntryArray, sizeof(aTarget.mWidth));
memcpy(&aTarget.mHeight, mDirEntryArray+1, sizeof(aTarget.mHeight)); memcpy(&aTarget.mHeight, mDirEntryArray + 1, sizeof(aTarget.mHeight));
memcpy(&aTarget.mColorCount, mDirEntryArray+2, sizeof(aTarget.mColorCount)); memcpy(&aTarget.mColorCount, mDirEntryArray + 2, sizeof(aTarget.mColorCount));
memcpy(&aTarget.mReserved, mDirEntryArray+3, sizeof(aTarget.mReserved)); memcpy(&aTarget.mReserved, mDirEntryArray + 3, sizeof(aTarget.mReserved));
memcpy(&aTarget.mPlanes, mDirEntryArray + 4, sizeof(aTarget.mPlanes));
memcpy(&aTarget.mPlanes, mDirEntryArray+4, sizeof(aTarget.mPlanes));
aTarget.mPlanes = LITTLE_TO_NATIVE16(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); 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); aTarget.mBytesInRes = LITTLE_TO_NATIVE32(aTarget.mBytesInRes);
memcpy(&aTarget.mImageOffset, mDirEntryArray + 12,
memcpy(&aTarget.mImageOffset, mDirEntryArray+12, sizeof(aTarget.mImageOffset)); sizeof(aTarget.mImageOffset));
aTarget.mImageOffset = LITTLE_TO_NATIVE32(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 imagelib
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -22,6 +22,7 @@
* Contributor(s): * Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author) * David Hyatt <hyatt@netscape.com> (Original Author)
* Bobby Holley <bobbyholley@gmail.com> * Bobby Holley <bobbyholley@gmail.com>
* Brian R. Bondy <netzen@gmail.com>
* *
* Alternatively, the contents of this file may be used under the terms of * 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 * either the GNU General Public License Version 2 or later (the "GPL"), or
@ -45,10 +46,15 @@
#include "Decoder.h" #include "Decoder.h"
#include "imgIDecoderObserver.h" #include "imgIDecoderObserver.h"
#include "nsBMPDecoder.h" #include "nsBMPDecoder.h"
#include "nsPNGDecoder.h"
namespace mozilla { namespace mozilla {
namespace imagelib { namespace imagelib {
#define ICODIRENTRYSIZE 16
#define PNGSIGNATURESIZE 8
#define BMPFILEHEADERSIZE 14
class RasterImage; class RasterImage;
struct IconDirEntry struct IconDirEntry
@ -80,37 +86,42 @@ public:
virtual void FinishInternal(); virtual void FinishInternal();
private: private:
// Private helper methods // Processes a single dir entry of the icon resource
void ProcessDirEntry(IconDirEntry& aTarget); void ProcessDirEntry(IconDirEntry& aTarget);
void ProcessInfoHeader(); // Sets the hotspot property of if we have a cursor
void SetHotSpotIfCursor();
nsresult SetImageData(); // 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(); PRUint32 CalcAlphaRowSize();
// Obtains the number of colors from the BPP, mBPP must be filled in
PRUint16 GetNumColors();
PRUint32 mPos; PRUint16 mBPP; // Stores the images BPP
PRUint16 mNumIcons; PRUint32 mPos; // Keeps track of the position we have decoded up until
PRUint16 mCurrIcon; PRUint16 mNumIcons; // Stores the number of icons in the ICO file
PRUint32 mImageOffset; PRUint16 mCurrIcon; // Stores the current dir entry index we are processing
PRUint32 mImageOffset; // Stores the offset of the image data we want
char mDirEntryArray[16]; PRUint8 *mRow; // Holds one raw line of the image
IconDirEntry mDirEntry; PRInt32 mCurLine; // Line index of the image that's currently being decoded
char mBIHraw[40];
BMPINFOHEADER mBIH;
PRUint32 mNumColors;
colorTable* mColors;
PRUint8* mRow; // Holds one raw line of the image
PRUint32 mRowBytes; // How many bytes of the row were already received PRUint32 mRowBytes; // How many bytes of the row were already received
PRInt32 mCurLine; PRInt32 mOldLine; // Previous index of the line
nsAutoPtr<Decoder> mContainedDecoder; // Contains either a BMP or PNG resource
PRUint32* mImageData; char mDirEntryArray[ICODIRENTRYSIZE]; // Holds the current dir entry buffer
IconDirEntry mDirEntry; // Holds a decoded dir entry
PRPackedBool mHaveAlphaData; // 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 mIsCursor;
PRPackedBool mDecodingAndMask; // Stores whether or not the contained resource is a PNG
PRPackedBool mIsPNG;
}; };
} // namespace imagelib } // namespace imagelib

Просмотреть файл

@ -77,10 +77,9 @@ static PRLogModuleInfo *gPNGDecoderAccountingLog =
#define HEIGHT_OFFSET (WIDTH_OFFSET + 4) #define HEIGHT_OFFSET (WIDTH_OFFSET + 4)
#define BYTES_NEEDED_FOR_DIMENSIONS (HEIGHT_OFFSET + 4) #define BYTES_NEEDED_FOR_DIMENSIONS (HEIGHT_OFFSET + 4)
// This is defined in the PNG spec as an invariant. We use it to // First 8 bytes of a PNG file
// do manual validation without libpng. const PRUint8
static const PRUint8 pngSignatureBytes[] = nsPNGDecoder::pngSignatureBytes[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
{ 137, 80, 78, 71, 13, 10, 26, 10 };
nsPNGDecoder::nsPNGDecoder() : nsPNGDecoder::nsPNGDecoder() :
mPNG(nsnull), mInfo(nsnull), mPNG(nsnull), mInfo(nsnull),
@ -317,7 +316,8 @@ nsPNGDecoder::WriteInternal(const char *aBuffer, PRUint32 aCount)
if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS) { if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS) {
// Check that the signature bytes are right // Check that the signature bytes are right
if (memcmp(mHeaderBuf, pngSignatureBytes, sizeof(pngSignatureBytes))) { if (memcmp(mHeaderBuf, nsPNGDecoder::pngSignatureBytes,
sizeof(pngSignatureBytes))) {
PostDataError(); PostDataError();
return; return;
} }

Просмотреть файл

@ -72,6 +72,21 @@ public:
void EndImageFrame(); 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: public:
png_structp mPNG; png_structp mPNG;
png_infop mInfo; png_infop mInfo;
@ -113,6 +128,10 @@ public:
png_const_charp error_msg); png_const_charp error_msg);
static void PNGAPI warning_callback(png_structp png_ptr, static void PNGAPI warning_callback(png_structp png_ptr,
png_const_charp warning_msg); 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 } // namespace imagelib

Просмотреть файл

@ -88,6 +88,26 @@ Decoder::Init(RasterImage* aImage, imgIDecoderObserver* aObserver)
mInitialized = true; 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 void
Decoder::Write(const char* aBuffer, PRUint32 aCount) Decoder::Write(const char* aBuffer, PRUint32 aCount)
{ {
@ -155,6 +175,14 @@ Decoder::Finish()
} }
} }
void
Decoder::FinishSharedDecoder()
{
if (!HasError()) {
FinishInternal();
}
}
void void
Decoder::FlushInvalidations() Decoder::FlushInvalidations()
{ {

Просмотреть файл

@ -63,6 +63,18 @@ public:
*/ */
void Init(RasterImage* aImage, imgIDecoderObserver* aObserver); 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. * Writes data to the decoder.
* *
@ -82,6 +94,14 @@ public:
*/ */
void Finish(); 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 * Tells the decoder to flush any pending invalidations. This informs the image
* frame of its decoded region, and sends the appropriate OnDataAvailable call * frame of its decoded region, and sends the appropriate OnDataAvailable call
@ -122,6 +142,9 @@ public:
bool HasDecoderError() { return NS_FAILED(mFailCode); }; bool HasDecoderError() { return NS_FAILED(mFailCode); };
nsresult GetDecoderError() { return mFailCode; }; nsresult GetDecoderError() { return mFailCode; };
void PostResizeError() { PostDataError(); } void PostResizeError() { PostDataError(); }
bool GetDecodeDone() const {
return mDecodeDone;
}
// flags. Keep these in sync with imgIContainer.idl. // flags. Keep these in sync with imgIContainer.idl.
// SetDecodeFlags must be called before Init(), otherwise // SetDecodeFlags must be called before Init(), otherwise
@ -176,12 +199,13 @@ protected:
* *
*/ */
nsRefPtr<RasterImage> mImage; nsRefPtr<RasterImage> mImage;
PRUint32 mDecodeFlags;
private:
nsCOMPtr<imgIDecoderObserver> mObserver; nsCOMPtr<imgIDecoderObserver> mObserver;
PRUint32 mDecodeFlags;
bool mDecodeDone;
bool mDataError;
private:
PRUint32 mFrameCount; // Number of frames, including anything in-progress PRUint32 mFrameCount; // Number of frames, including anything in-progress
nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame. nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
@ -191,8 +215,6 @@ private:
bool mInitialized; bool mInitialized;
bool mSizeDecode; bool mSizeDecode;
bool mInFrame; bool mInFrame;
bool mDecodeDone;
bool mDataError;
}; };
} // namespace imagelib } // namespace imagelib