зеркало из https://github.com/mozilla/pjs.git
Bug 600556 - Support Vista-style PNG ICO files. r=joe
This commit is contained in:
Родитель
c6dbea4d8c
Коммит
e8392a91ce
|
@ -22,6 +22,7 @@
|
|||
* Contributor(s):
|
||||
* Neil Rashbrook <neil@parkwaycc.co.uk>
|
||||
* Bobby Holley <bobbyholley@gmail.com>
|
||||
* Brian R. Bondy <netzen@gmail.com>
|
||||
*
|
||||
* 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) {
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Bobby Holley <bobbyholley@gmail.com>
|
||||
* Brian R. Bondy <netzen@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
* David Hyatt <hyatt@netscape.com> (Original Author)
|
||||
* Christian Biesinger <cbiesinger@web.de>
|
||||
* Bobby Holley <bobbyholley@gmail.com>
|
||||
* Brian R. Bondy <netzen@gmail.com>
|
||||
*
|
||||
* 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<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.
|
||||
|
||||
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<nsPNGDecoder*>(mContainedDecoder.get())->HasValidInfo() &&
|
||||
static_cast<nsPNGDecoder*>(mContainedDecoder.get())->GetPixelDepth() != 32) {
|
||||
PostDataError();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We've processed all of the icon dir entries and are within the
|
||||
// bitmap info size
|
||||
if (!mIsPNG && mCurrIcon == mNumIcons && mPos >= mImageOffset &&
|
||||
mPos >= mImageOffset + PNGSIGNATURESIZE &&
|
||||
mPos < mImageOffset + BITMAPINFOSIZE) {
|
||||
|
||||
// As we were decoding, we did not know if we had a PNG signature or the
|
||||
// start of a bitmap information header. At this point we know we had
|
||||
// a bitmap information header and not a PNG signature, so fill the bitmap
|
||||
// information header with the data it should already have.
|
||||
memcpy(mBIHraw, mSignature, PNGSIGNATURESIZE);
|
||||
|
||||
// We've found the icon.
|
||||
PRUint32 toCopy = sizeof(mBIHraw) - (mPos - mImageOffset);
|
||||
if (toCopy > aCount)
|
||||
|
@ -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<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) {
|
||||
// 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<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);
|
||||
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<decoded_end; bit >>= 1) {
|
||||
// Clear pixel completely for transparency.
|
||||
if (idx & bit) *decoded = 0;
|
||||
decoded ++;
|
||||
PRUint32* imageData = static_cast<nsBMPDecoder*>(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<decoded_end; bit >>= 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
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Contributor(s):
|
||||
* David Hyatt <hyatt@netscape.com> (Original Author)
|
||||
* Bobby Holley <bobbyholley@gmail.com>
|
||||
* Brian R. Bondy <netzen@gmail.com>
|
||||
*
|
||||
* 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<Decoder> 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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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<RasterImage> mImage;
|
||||
|
||||
PRUint32 mDecodeFlags;
|
||||
|
||||
private:
|
||||
nsCOMPtr<imgIDecoderObserver> 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
|
||||
|
|
Загрузка…
Ссылка в новой задаче