Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2083 lines
50 KiB

#include "stdafx.h"
#include "pngfilt.h"
#include "resource.h"
#include "cpngfilt.h"
#include "scanline.h"
#include <math.h>
#include "pngcrc.cpp"
#undef DEFINE_GUID
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
EXTERN_C const GUID name \
= { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
DEFINE_GUID( IID_IDirectDrawSurface, 0x6C14DB81,0xA733,0x11CE,0xA5,0x21,0x00,
0x20,0xAF,0x0B,0xE5,0x60 );
#ifdef _DEBUG
static ULONG g_nPNGTraceLevel = 1;
static void _cdecl FakeTrace( LPCTSTR pszFormat, ... )
{
(void)pszFormat;
}
#define PNGTRACE1(PARAMS) ((g_nPNGTraceLevel >= 1) ? AtlTrace##PARAMS : FakeTrace##PARAMS)
#define PNGTRACE(PARAMS) ATLTRACE##PARAMS
#else
#define PNGTRACE1(PARAMS) ATLTRACE##PARAMS
#define PNGTRACE(PARAMS) ATLTRACE##PARAMS
#endif
// Gamma correction table for sRGB, assuming a file gamma of 1.0
#define VIEWING_GAMMA 1.125
#define DISPLAY_GAMMA 2.2
#define MAXFBVAL 255
BYTE gamma10[256] = {
0, 15, 21, 26, 30, 34, 37, 41, 43, 46, 49, 51, 53, 56, 58, 60,
62, 64, 66, 68, 69, 71, 73, 75, 76, 78, 79, 81, 82, 84, 85, 87,
88, 90, 91, 92, 94, 95, 96, 98, 99,100,101,103,104,105,106,107,
109,110,111,112,113,114,115,116,117,119,120,121,122,123,124,125,
126,127,128,129,130,131,132,133,134,135,135,136,137,138,139,140,
141,142,143,144,145,145,146,147,148,149,150,151,151,152,153,154,
155,156,156,157,158,159,160,160,161,162,163,164,164,165,166,167,
167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,179,
179,180,181,181,182,183,184,184,185,186,186,187,188,188,189,190,
190,191,192,192,193,194,194,195,196,196,197,198,198,199,200,200,
201,202,202,203,203,204,205,205,206,207,207,208,208,209,210,210,
211,212,212,213,213,214,215,215,216,216,217,218,218,219,219,220,
221,221,222,222,223,223,224,225,225,226,226,227,228,228,229,229,
230,230,231,231,232,233,233,234,234,235,235,236,236,237,238,238,
239,239,240,240,241,241,242,242,243,244,244,245,245,246,246,247,
247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255,
};
#ifdef BIG_ENDIAN
#define my_ntohl(x) (x)
inline DWORD endianConverter( DWORD dwSrc )
{
return( ((dwSrc&0xff)<<24)+((dwSrc&0xff00)<<8)+((dwSrc&0xff0000)>>8)+
((dwSrc&0xff000000)>>24) );
}
#else
inline DWORD my_ntohl( DWORD dwSrc )
{
return( ((dwSrc&0xff)<<24)+((dwSrc&0xff00)<<8)+((dwSrc&0xff0000)>>8)+
((dwSrc&0xff000000)>>24) );
}
#endif
void FixByteOrder( PNGCHUNKHEADER* pChunkHeader )
{
pChunkHeader->nDataLength = my_ntohl( pChunkHeader->nDataLength );
}
void FixByteOrder( PNGIHDRDATA* pIHDR )
{
pIHDR->nWidth = my_ntohl( pIHDR->nWidth );
pIHDR->nHeight = my_ntohl( pIHDR->nHeight );
}
void CopyPaletteEntriesFromColors(PALETTEENTRY *ppe, const RGBQUAD *prgb,
UINT uCount)
{
while (uCount--)
{
ppe->peRed = prgb->rgbRed;
ppe->peGreen = prgb->rgbGreen;
ppe->peBlue = prgb->rgbBlue;
ppe->peFlags = 0;
prgb++;
ppe++;
}
}
/////////////////////////////////////////////////////////////////////////////
//
CPNGFilter::CPNGFilter() :
m_eInternalState( ISTATE_READFILEHEADER ),
m_nBytesLeftInCurrentTask( 0 ),
m_nDataBytesRead( 0 ),
m_iAppend( 0 ),
m_pStream( NULL ),
m_bFinishedIDAT( FALSE ),
m_nBytesInScanLine( 0 ),
m_iScanLine( 0 ),
m_iFirstStaleScanLine( 0 ),
m_bExpandPixels( FALSE ),
m_pbScanLine( NULL ),
m_pbPrevScanLine( NULL ),
m_pfnCopyScanLine( NULL ),
m_dwChunksEncountered( 0 ),
m_iBackgroundIndex( 0 ),
m_bSurfaceUsesAlpha( FALSE ),
m_bConvertAlpha( FALSE ),
m_nFormats( 0 ),
m_pFormats( NULL ),
m_nTransparentColors( 0 )
{
m_rgbBackground.rgbRed = 0;
m_rgbBackground.rgbGreen = 0;
m_rgbBackground.rgbBlue = 0;
m_rgbBackground.rgbReserved = 0;
for (int i = 0; i < 256; ++i)
m_abTrans[i] = (BYTE)i;
memcpy(m_abGamma, m_abTrans, sizeof(m_abTrans));
}
CPNGFilter::~CPNGFilter()
{
if( m_pFormats != NULL )
{
CoTaskMemFree( m_pFormats );
}
delete m_pbPrevScanLine;
delete m_pbScanLine;
}
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineGray1[1] =
{
CopyScanLineGray1ToBGR24
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineGray2[1] =
{
CopyScanLineGray2ToBGR24
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineGray4[1] =
{
CopyScanLineGray4ToBGR24
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineGray8[1] =
{
CopyScanLineGray8ToBGR24
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineGray16[1] =
{
CopyScanLineGray16ToBGR24
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineRGB24[1] =
{
CopyScanLineRGB24ToBGR24
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineRGB48[1] =
{
CopyScanLineRGB48ToBGR24
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineIndex1[1] =
{
CopyScanLineIndex1ToIndex8
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineIndex2[1] =
{
CopyScanLineIndex2ToIndex8
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineIndex4[1] =
{
CopyScanLineIndex4ToIndex8
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineIndex8[1] =
{
CopyScanLineIndex8ToIndex8
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineGrayA16[2] =
{
CopyScanLineGrayA16ToBGRA32,
CopyScanLineGrayA16ToBGR24
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineGrayA32[2] =
{
CopyScanLineGrayA32ToBGRA32,
CopyScanLineGrayA32ToBGR24
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineRGBA32[2] =
{
CopyScanLineRGBA32ToBGRA32,
CopyScanLineRGBA32ToBGR24
};
const PNGCOPYSCANLINEPROC g_apfnCopyScanLineRGBA64[2] =
{
CopyScanLineRGBA64ToBGRA32,
CopyScanLineRGBA64ToBGR24
};
const PNGDUPLICATESCANLINEPROC g_apfnDuplicateScanLineBGR24[1] =
{
DuplicateScanLineBGR24
};
const PNGDUPLICATESCANLINEPROC g_apfnDuplicateScanLineIndex8[1] =
{
DuplicateScanLineIndex8
};
const PNGDUPLICATESCANLINEPROC g_apfnDuplicateScanLineAlphaSrc[2] =
{
DuplicateScanLineARGB32,
DuplicateScanLineBGR24
};
const GUID g_TargetGuidsForAlphaSrcs[2] =
{
// BFID_RGBA_32
{ 0x773c9ac0, 0x3274, 0x11d0, { 0xb7, 0x24, 0x00, 0xaa, 0x00, 0x6c, 0x1a, 0x1 } },
// BFID_RGB_24
{ 0xe436eb7d, 0x524f, 0x11ce, { 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 } }
};
const PNG_FORMAT_INFO CPNGFilter::s_aFormatInfo[15] =
{
{ 1, &BFID_RGB_24, g_apfnCopyScanLineGray1, g_apfnDuplicateScanLineBGR24 },
{ 1, &BFID_RGB_24, g_apfnCopyScanLineGray2, g_apfnDuplicateScanLineBGR24 },
{ 1, &BFID_RGB_24, g_apfnCopyScanLineGray4, g_apfnDuplicateScanLineBGR24 },
{ 1, &BFID_RGB_24, g_apfnCopyScanLineGray8, g_apfnDuplicateScanLineBGR24 },
{ 1, &BFID_RGB_24, g_apfnCopyScanLineGray16, g_apfnDuplicateScanLineBGR24 },
{ 1, &BFID_RGB_24, g_apfnCopyScanLineRGB24, g_apfnDuplicateScanLineBGR24 },
{ 1, &BFID_RGB_24, g_apfnCopyScanLineRGB48, g_apfnDuplicateScanLineBGR24 },
{ 1, &BFID_INDEXED_RGB_8, g_apfnCopyScanLineIndex1, g_apfnDuplicateScanLineIndex8 },
{ 1, &BFID_INDEXED_RGB_8, g_apfnCopyScanLineIndex2, g_apfnDuplicateScanLineIndex8 },
{ 1, &BFID_INDEXED_RGB_8, g_apfnCopyScanLineIndex4, g_apfnDuplicateScanLineIndex8 },
{ 1, &BFID_INDEXED_RGB_8, g_apfnCopyScanLineIndex8, g_apfnDuplicateScanLineIndex8 },
{ 2, g_TargetGuidsForAlphaSrcs, g_apfnCopyScanLineGrayA16, g_apfnDuplicateScanLineAlphaSrc },
{ 2, g_TargetGuidsForAlphaSrcs, g_apfnCopyScanLineGrayA32, g_apfnDuplicateScanLineAlphaSrc },
{ 2, g_TargetGuidsForAlphaSrcs, g_apfnCopyScanLineRGBA32, g_apfnDuplicateScanLineAlphaSrc },
{ 2, g_TargetGuidsForAlphaSrcs, g_apfnCopyScanLineRGBA64, g_apfnDuplicateScanLineAlphaSrc }
};
HRESULT CPNGFilter::ChooseDestinationFormat( GUID* pBFID )
{
ULONG iPossibleFormat;
ULONG iAcceptableFormat;
const PNG_FORMAT_INFO* pFormatInfo;
BOOL bFound;
ULONG iChosenFormat;
_ASSERTE( pBFID != NULL );
*pBFID = GUID_NULL;
pFormatInfo = &s_aFormatInfo[m_eSrcFormat];
bFound = FALSE;
iChosenFormat = 0;
for( iAcceptableFormat = 0; (iAcceptableFormat < m_nFormats) && !bFound;
iAcceptableFormat++ )
{
for( iPossibleFormat = 0; iPossibleFormat <
pFormatInfo->nPossibleFormats; iPossibleFormat++ )
{
if( IsEqualGUID(m_pFormats[iAcceptableFormat],
pFormatInfo->pPossibleFormats[iPossibleFormat] ) )
{
iChosenFormat = iPossibleFormat;
bFound = TRUE;
}
}
}
if( !bFound )
{
return( E_FAIL );
}
m_pfnCopyScanLine = pFormatInfo->ppfnCopyScanLineProcs[iChosenFormat];
m_pfnDuplicateScanLine = pFormatInfo->ppfnDuplicateScanLineProcs[iChosenFormat];
*pBFID = pFormatInfo->pPossibleFormats[iChosenFormat];
return( S_OK );
}
HRESULT CPNGFilter::LockBits(RECT *prcBounds, DWORD dwLockFlags, void **ppBits, long *pPitch)
{
HRESULT hResult;
DDSURFACEDESC ddsd;
(dwLockFlags);
ddsd.dwSize = sizeof(ddsd);
hResult = m_pDDrawSurface->Lock(prcBounds, &ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL);
if (FAILED(hResult))
return hResult;
*ppBits = ddsd.lpSurface;
*pPitch = ddsd.lPitch;
return S_OK;
}
HRESULT CPNGFilter::UnlockBits(RECT *prcBounds, void *pBits)
{
(prcBounds);
return m_pDDrawSurface->Unlock(pBits);
}
HRESULT CPNGFilter::FireGetSurfaceEvent()
{
HRESULT hResult;
GUID bfid;
CComPtr < IUnknown > pSurface;
_ASSERTE( m_pEventSink != NULL );
_ASSERTE( m_pDDrawSurface == NULL );
m_bConvertAlpha = FALSE;
m_bSurfaceUsesAlpha = FALSE;
hResult = ChooseDestinationFormat(&bfid);
if (FAILED(hResult))
{
return(hResult);
}
hResult = m_pEventSink->GetSurface(m_pngIHDR.nWidth, m_pngIHDR.nHeight,
bfid, m_nPasses, IMGDECODE_HINT_TOPDOWN|IMGDECODE_HINT_FULLWIDTH,
&pSurface);
if (FAILED(hResult))
{
return( hResult);
}
hResult = pSurface->QueryInterface(IID_IDirectDrawSurface, (void **)&m_pDDrawSurface);
if (FAILED(hResult))
return(hResult);
return (S_OK);
}
// Send an OnProgress event to the event sink (if it has requested progress
// notifications).
HRESULT CPNGFilter::FireOnProgressEvent()
{
HRESULT hResult;
RECT rect;
if( !(m_dwEvents & IMGDECODE_EVENT_PROGRESS) )
{
return( S_OK );
}
PNGTRACE1((_T("Pass: %d\n"), m_iPass ));
rect.left = 0;
rect.top = m_iFirstStaleScanLine;
rect.right = m_pngIHDR.nWidth;
rect.bottom = min( m_iScanLine, m_pngIHDR.nHeight );
hResult = m_pEventSink->OnProgress( &rect, TRUE );
if( FAILED( hResult ) )
{
return( hResult );
}
m_iFirstStaleScanLine = m_iScanLine;
return( S_OK );
}
///////////////////////////////////////////////////////////////////////////////
// PNG scan line filtering routines
void CPNGFilter::NoneFilterScanLine()
{
}
void CPNGFilter::SubFilterScanLine()
{
BYTE* pbByte;
ULONG iByte;
ULONG nSrcByte;
pbByte = m_pbScanLine+1+m_nBPP;
for( iByte = m_nBPP; iByte < m_nBytesInScanLine; iByte++ )
{
nSrcByte = *pbByte;
nSrcByte += *(pbByte-m_nBPP);
*pbByte = BYTE( nSrcByte );
pbByte++;
}
}
void CPNGFilter::UpFilterScanLine()
{
ULONG iByte;
if( m_iScanLineInPass == 0 )
{
// Unfiltering the top scan line is a NOP
return;
}
for( iByte = 1; iByte <= m_nBytesInScanLine; iByte++ )
{
m_pbScanLine[iByte] = BYTE( m_pbScanLine[iByte]+
m_pbPrevScanLine[iByte] );
}
}
void CPNGFilter::AverageFilterScanLine()
{
ULONG iByte;
ULONG nSum;
if( m_iScanLineInPass == 0 )
{
// No prior scan line. Skip the first m_nBPP bytes, since they are not
// affected by the filter
for( iByte = m_nBPP+1; iByte <= m_nBytesInScanLine; iByte++ )
{
m_pbScanLine[iByte] = BYTE( m_pbScanLine[iByte]+
(m_pbScanLine[iByte-m_nBPP]/2) );
}
}
else
{
for( iByte = 1; iByte <= m_nBPP; iByte++ )
{
m_pbScanLine[iByte] = BYTE( m_pbScanLine[iByte]+
(m_pbPrevScanLine[iByte]/2) );
}
for( ; iByte <= m_nBytesInScanLine; iByte++ )
{
nSum = m_pbScanLine[iByte-m_nBPP]+m_pbPrevScanLine[iByte];
m_pbScanLine[iByte] = BYTE( m_pbScanLine[iByte]+(nSum/2) );
}
}
}
static inline int Abs( int n )
{
if( n > 0 )
{
return( n );
}
else
{
return( -n );
}
}
BYTE PaethPredictor( BYTE a, BYTE b, BYTE c )
{
int p;
int pa;
int pb;
int pc;
p = int( a )+int( b )-int( c );
pa = Abs( p-a );
pb = Abs( p-b );
pc = Abs( p-c );
if( (pa <= pb) && (pa <= pc) )
{
return( a );
}
if( pb <= pc )
{
return( b );
}
return( c );
}
void CPNGFilter::PaethFilterScanLine()
{
ULONG iByte;
if( m_iScanLineInPass == 0 )
{
for( iByte = 1; iByte <= m_nBPP; iByte++ )
{
m_pbScanLine[iByte] = BYTE( m_pbScanLine[iByte]+PaethPredictor( 0, 0,
0 ) );
}
for( ; iByte <= m_nBytesInScanLine; iByte++ )
{
m_pbScanLine[iByte] = BYTE( m_pbScanLine[iByte]+PaethPredictor(
m_pbScanLine[iByte-m_nBPP], 0, 0 ) );
}
}
else
{
for( iByte = 1; iByte <= m_nBPP; iByte++ )
{
m_pbScanLine[iByte] = BYTE( m_pbScanLine[iByte]+PaethPredictor( 0,
m_pbPrevScanLine[iByte], 0 ) );
}
for( ; iByte <= m_nBytesInScanLine; iByte++ )
{
m_pbScanLine[iByte] = BYTE( m_pbScanLine[iByte]+PaethPredictor(
m_pbScanLine[iByte-m_nBPP], m_pbPrevScanLine[iByte],
m_pbPrevScanLine[iByte-m_nBPP] ) );
}
}
}
// Update a CRC accumulator with new data bytes
DWORD UpdateCRC( DWORD dwInitialCRC, const BYTE* pbData, ULONG nCount )
{
DWORD dwCRC;
ULONG iByte;
dwCRC = dwInitialCRC;
for( iByte = 0; iByte < nCount; iByte++ )
{
dwCRC = g_adwCRCTable[(dwCRC^pbData[iByte])&0xff]^(dwCRC>>8);
}
return( dwCRC );
}
HRESULT CPNGFilter::NextState()
{
switch( m_eInternalState )
{
case ISTATE_READFILEHEADER:
m_eInternalState = ISTATE_READCHUNKHEADER;
break;
case ISTATE_READCHUNKHEADER:
if( m_pngChunkHeader.dwChunkType == PNG_CHUNK_IDAT )
{
if (!(m_dwChunksEncountered & CHUNK_BKGD))
m_eInternalState = ISTATE_CHOOSEBKGD;
else
m_eInternalState = ISTATE_READIDATDATA;
}
else
{
m_eInternalState = ISTATE_READCHUNKDATA;
}
break;
case ISTATE_CHOOSEBKGD:
m_eInternalState = ISTATE_READIDATDATA;
break;
case ISTATE_READCHUNKDATA:
if (m_bSkipData)
{
m_eInternalState = ISTATE_EATDATA;
}
else switch( m_pngChunkHeader.dwChunkType )
{
case PNG_CHUNK_BKGD:
m_eInternalState = ISTATE_PROCESSBKGD;
break;
case PNG_CHUNK_IHDR:
m_eInternalState = ISTATE_PROCESSIHDR;
break;
case PNG_CHUNK_TRNS:
m_eInternalState = ISTATE_PROCESSTRNS;
break;
case PNG_CHUNK_GAMA:
m_eInternalState = ISTATE_PROCESSGAMA;
break;
case PNG_CHUNK_PLTE:
m_eInternalState = ISTATE_PROCESSPLTE;
break;
case PNG_CHUNK_IEND:
m_eInternalState = ISTATE_PROCESSIEND;
break;
case PNG_CHUNK_IDAT:
_ASSERT( FALSE );
// fallthrough
default:
m_eInternalState = ISTATE_EATDATA;
break;
}
break;
case ISTATE_PROCESSBKGD:
m_eInternalState = ISTATE_READCHUNKCRC;
break;
case ISTATE_PROCESSIHDR:
m_eInternalState = ISTATE_READCHUNKCRC;
break;
case ISTATE_PROCESSIEND:
m_eInternalState = ISTATE_READCHUNKCRC;
break;
case ISTATE_EATDATA:
if( m_nDataBytesRead != m_pngChunkHeader.nDataLength )
{
m_eInternalState = ISTATE_READCHUNKDATA;
}
else
{
m_eInternalState = ISTATE_READCHUNKCRC;
}
break;
case ISTATE_READCHUNKCRC:
if( m_dwChunksEncountered & CHUNK_IEND )
{
m_eInternalState = ISTATE_DONE;
}
else
{
m_eInternalState = ISTATE_READCHUNKHEADER;
}
break;
case ISTATE_READIDATDATA:
if( m_nDataBytesRead < m_pngChunkHeader.nDataLength )
{
m_eInternalState = ISTATE_READIDATDATA;
}
else
{
_ASSERTE( m_nDataBytesRead == m_pngChunkHeader.nDataLength );
m_eInternalState = ISTATE_READCHUNKCRC;
}
break;
case ISTATE_PROCESSPLTE:
m_eInternalState = ISTATE_READCHUNKCRC;
break;
case ISTATE_PROCESSTRNS:
m_eInternalState = ISTATE_READCHUNKCRC;
break;
case ISTATE_PROCESSGAMA:
m_eInternalState = ISTATE_READCHUNKCRC;
break;
case ISTATE_DONE:
m_eInternalState = ISTATE_DONE;
break;
default:
PNGTRACE((_T("Unknown state\n")));
_ASSERT( FALSE );
break;
}
m_nBytesLeftInCurrentTask = 0;
return( S_OK );
}
// Process a PNG background color chunk.
HRESULT CPNGFilter::ProcessBKGD()
{
if( !(m_dwChunksEncountered & CHUNK_IHDR) )
{
PNGTRACE((_T("Missing IHDR\n")));
return( E_FAIL );
}
if( m_dwChunksEncountered & CHUNK_BKGD )
{
PNGTRACE((_T("Multiple bKGD chunks\n")));
return( E_FAIL );
}
if( m_dwChunksEncountered & (CHUNK_IDAT|CHUNK_IEND) )
{
PNGTRACE((_T("Invalid bKGD placement\n")));
return( E_FAIL );
}
if( m_bPalette && !(m_dwChunksEncountered & CHUNK_PLTE) )
{
PNGTRACE((_T("bKGD before PLTE in indexed-color image\n")));
return( E_FAIL );
}
m_dwChunksEncountered |= (CHUNK_BKGD|CHUNK_POSTPLTE);
switch( m_pngIHDR.bColorType )
{
case PNG_COLORTYPE_INDEXED:
if( m_pngChunkHeader.nDataLength != 1 )
{
PNGTRACE((_T("Invalid bKGD size\n")));
return( E_FAIL );
}
m_iBackgroundIndex = m_abData[0];
if( m_iBackgroundIndex >= m_nColors )
{
PNGTRACE((_T("Invalid palette index in bKGD\n")));
return( E_FAIL );
}
break;
case PNG_COLORTYPE_RGB:
case PNG_COLORTYPE_RGBA:
if( m_pngChunkHeader.nDataLength != 6 )
{
PNGTRACE((_T("Invalid bKGD size\n")));
return( E_FAIL );
}
if( m_pngIHDR.nBitDepth == 8 )
{
m_frgbBackground.fRed = float( m_abData[1]/255.0 );
m_frgbBackground.fGreen = float( m_abData[3]/255.0 );
m_frgbBackground.fBlue = float( m_abData[5]/255.0 );
}
else
{
m_frgbBackground.fRed = float( ((m_abData[0]<<8)+m_abData[1] )/
65535.0 );
m_frgbBackground.fGreen = float( ((m_abData[2]<<8)+m_abData[3] )/
65535.0 );
m_frgbBackground.fBlue = float( ((m_abData[4]<<8)+m_abData[5] )/
65535.0 );
}
break;
case PNG_COLORTYPE_GRAY:
case PNG_COLORTYPE_GRAYA:
if( m_pngChunkHeader.nDataLength != 2 )
{
PNGTRACE((_T("Invalid bKGD size\n")));
return( E_FAIL );
}
m_frgbBackground.fRed = float( ((m_abData[0]<<8)+m_abData[1])&
((0x01<<m_pngIHDR.nBitDepth)-1) );
m_frgbBackground.fRed /= float( (0x01<<m_pngIHDR.nBitDepth)-1 );
m_frgbBackground.fGreen = m_frgbBackground.fRed;
m_frgbBackground.fBlue = m_frgbBackground.fRed;
break;
default:
_ASSERT( FALSE );
break;
}
m_rgbBackground.rgbRed = BYTE( m_frgbBackground.fRed*255.0 );
m_rgbBackground.rgbGreen = BYTE( m_frgbBackground.fGreen*255.0 );
m_rgbBackground.rgbBlue = BYTE( m_frgbBackground.fBlue*255.0 );
m_rgbBackground.rgbReserved = 0;
return( S_OK );
}
HRESULT CPNGFilter::ChooseBKGD()
{
if( !(m_dwChunksEncountered & CHUNK_IHDR) )
{
PNGTRACE((_T("Missing IHDR\n")));
return( E_FAIL );
}
if( m_dwChunksEncountered & CHUNK_BKGD )
{
PNGTRACE((_T("Multiple bKGD chunks\n")));
return( E_FAIL );
}
m_dwChunksEncountered |= (CHUNK_BKGD|CHUNK_POSTPLTE);
// Since the image doesn't specify a background color we have to
// choose one. Since the image target isn't known we'll use the
// button face color for lack of anything better...
*((DWORD *)&m_rgbBackground) = (GetSysColor(COLOR_BTNFACE) & 0x00FFFFFF);
m_frgbBackground.fRed = float( m_rgbBackground.rgbRed/255.0 );
m_frgbBackground.fGreen = float( m_rgbBackground.rgbGreen/255.0 );
m_frgbBackground.fBlue = float( m_rgbBackground.rgbBlue/255.0 );
return S_OK;
}
// Get ready to read the image data
HRESULT CPNGFilter::BeginImage()
{
LPDIRECTDRAWPALETTE pDDPalette;
PALETTEENTRY ape[256];
HRESULT hResult;
BYTE *pby;
int i;
// Nothing to do if there's no palette
if (!m_bPalette)
return S_OK;
if (!(m_dwChunksEncountered & CHUNK_PLTE))
{
PNGTRACE((_T("No PLTE chunk found for indexed color image\n")));
return (E_FAIL);
}
// TRICK: This applies gamma to the rgbReserved field as well
// but this field is always 0, and gamma correction of
// 0 is always 0, so this safe.
pby = (BYTE *)m_argbColors;
for (i = m_nColors * 4; i ; --i, ++pby)
*pby = m_abGamma[*pby];
hResult = m_pDDrawSurface->GetPalette(&pDDPalette);
if (SUCCEEDED(hResult))
{
CopyPaletteEntriesFromColors(ape, m_argbColors, m_nColors);
pDDPalette->SetEntries(0, 0, m_nColors, ape);
pDDPalette->Release();
}
if (m_dwEvents & IMGDECODE_EVENT_PALETTE)
{
hResult = m_pEventSink->OnPalette();
if (FAILED(hResult))
{
return (hResult);
}
}
return (S_OK);
}
// Process the PNG end-of-stream chunk
HRESULT CPNGFilter::ProcessIEND()
{
if( !(m_dwChunksEncountered & CHUNK_LASTIDAT) )
{
PNGTRACE((_T("Invalid IEND placement\n")));
return( E_FAIL );
}
m_dwChunksEncountered |= CHUNK_IEND;
if( m_pngChunkHeader.nDataLength > 0 )
{
PNGTRACE((_T("Invalid IEND chunk length\n")));
return( E_FAIL );
}
return( S_OK );
}
HRESULT CPNGFilter::DetermineSourceFormat()
{
switch( m_pngIHDR.bColorType )
{
case PNG_COLORTYPE_RGB:
switch( m_pngIHDR.nBitDepth )
{
case 8:
m_eSrcFormat = SRC_RGB_24;
break;
case 16:
m_eSrcFormat = SRC_RGB_48;
break;
default:
PNGTRACE((_T("Invalid bit depth %d for RGB image\n"),
m_pngIHDR.nBitDepth ));
return( E_FAIL );
break;
}
m_nBitsPerPixel = m_pngIHDR.nBitDepth*3;
break;
case PNG_COLORTYPE_RGBA:
switch( m_pngIHDR.nBitDepth )
{
case 8:
m_eSrcFormat = SRC_RGBA_32;
break;
case 16:
m_eSrcFormat = SRC_RGBA_64;
break;
default:
PNGTRACE((_T("Invalid bit depth %d for RGBA image\n"),
m_pngIHDR.nBitDepth ));
return( E_FAIL );
break;
}
m_nBitsPerPixel = m_pngIHDR.nBitDepth*4;
break;
case PNG_COLORTYPE_INDEXED:
switch( m_pngIHDR.nBitDepth )
{
case 1:
m_eSrcFormat = SRC_INDEXED_RGB_1;
break;
case 2:
m_eSrcFormat = SRC_INDEXED_RGB_2;
break;
case 4:
m_eSrcFormat = SRC_INDEXED_RGB_4;
break;
case 8:
m_eSrcFormat = SRC_INDEXED_RGB_8;
break;
default:
PNGTRACE((_T("Invalid bit depth %d for indexed-color image\n"),
m_pngIHDR.nBitDepth ));
return( E_FAIL );
break;
}
m_nBitsPerPixel = m_pngIHDR.nBitDepth;
break;
case PNG_COLORTYPE_GRAY:
switch( m_pngIHDR.nBitDepth )
{
case 1:
m_eSrcFormat = SRC_GRAY_1;
break;
case 2:
m_eSrcFormat = SRC_GRAY_2;
break;
case 4:
m_eSrcFormat = SRC_GRAY_4;
break;
case 8:
m_eSrcFormat = SRC_GRAY_8;
break;
case 16:
m_eSrcFormat = SRC_GRAY_16;
break;
default:
PNGTRACE((_T("Invalid bit depth %d for grayscale image\n"),
m_pngIHDR.nBitDepth ));
return( E_FAIL );
break;
}
m_nBitsPerPixel = m_pngIHDR.nBitDepth;
break;
case PNG_COLORTYPE_GRAYA:
switch( m_pngIHDR.nBitDepth )
{
case 8:
m_eSrcFormat = SRC_GRAYA_16;
break;
case 16:
m_eSrcFormat = SRC_GRAYA_32;
break;
default:
PNGTRACE((_T("Invalid bit depth %d for grayscale/alpha image\n"),
m_pngIHDR.nBitDepth ));
return( E_FAIL );
break;
}
m_nBitsPerPixel = m_pngIHDR.nBitDepth*2;
break;
default:
PNGTRACE((_T("Invalid color type %d\n"), m_pngIHDR.bColorType ));
return( E_FAIL );
break;
}
return( S_OK );
}
// Process the PNG image header chunk
HRESULT CPNGFilter::ProcessIHDR()
{
PNGIHDRDATA* pIHDR;
HRESULT hResult;
int nError;
if( m_dwChunksEncountered != 0 )
{
PNGTRACE((_T("Multiple IHDR chunks\n")));
return( E_FAIL );
}
m_dwChunksEncountered |= CHUNK_IHDR;
pIHDR = (PNGIHDRDATA*)m_abData;
FixByteOrder( pIHDR );
memcpy( &m_pngIHDR, pIHDR, sizeof( m_pngIHDR ) );
PNGTRACE1((_T("%dx%dx%d\n"), m_pngIHDR.nWidth, m_pngIHDR.nHeight,
m_pngIHDR.nBitDepth ));
if( (m_pngIHDR.nWidth == 0) || (m_pngIHDR.nHeight == 0) )
{
PNGTRACE((_T("Invalid image size\n")));
return( E_FAIL );
}
m_bPalette = m_pngIHDR.bColorType & PNG_COLORTYPE_PALETTE_MASK;
m_bColor = m_pngIHDR.bColorType & PNG_COLORTYPE_COLOR_MASK;
m_bAlpha = m_pngIHDR.bColorType & PNG_COLORTYPE_ALPHA_MASK;
hResult = DetermineSourceFormat();
if( FAILED( hResult ) )
{
return( hResult );
}
m_nBytesInScanLine = ((m_pngIHDR.nWidth*m_nBitsPerPixel)+7)/8;
m_nBPP = max( 1, m_nBytesInScanLine/m_pngIHDR.nWidth );
m_pbPrevScanLine = new BYTE[m_nBytesInScanLine+1];
if( m_pbPrevScanLine == NULL )
{
return( E_OUTOFMEMORY );
}
m_pbScanLine = new BYTE[m_nBytesInScanLine+1];
if( m_pbScanLine == NULL )
{
return( E_OUTOFMEMORY );
}
switch( m_pngIHDR.bCompressionMethod )
{
case PNG_COMPRESSION_DEFLATE32K:
m_zlibStream.zalloc = NULL;
m_zlibStream.zfree = NULL;
m_zlibStream.opaque = NULL;
nError = inflateInit( &m_zlibStream );
if( nError != Z_OK )
{
return( E_OUTOFMEMORY );
}
break;
default:
PNGTRACE((_T("Unknown compression method %x\n"),
m_pngIHDR.bCompressionMethod ));
return( E_FAIL );
break;
}
if( m_pngIHDR.bFilterMethod != PNG_FILTER_ADAPTIVE )
{
PNGTRACE((_T("Unknown filter method %x\n"), m_pngIHDR.bFilterMethod ));
return( E_FAIL );
}
switch( m_pngIHDR.bInterlaceMethod )
{
case PNG_INTERLACE_NONE:
PNGTRACE1((_T("Image is not interlaced\n")));
m_nPasses = 1;
m_pInterlaceInfo = s_aInterlaceInfoNone;
m_bExpandPixels = FALSE;
break;
case PNG_INTERLACE_ADAM7:
PNGTRACE1((_T("Image is Adam7 interlaced\n")));
m_nPasses = 7;
m_pInterlaceInfo = s_aInterlaceInfoAdam7;
if( m_dwEvents & IMGDECODE_EVENT_PROGRESS )
{
m_bExpandPixels = TRUE;
}
else
{
// Don't bother expanding the pixels if the event sink doesn't care
// about progress messages.
m_bExpandPixels = FALSE;
}
break;
default:
PNGTRACE((_T("Unknown interlace method %d\n"), m_pngIHDR.bInterlaceMethod ));
return( E_FAIL );
break;
}
m_iPass = 0;
BeginPass( m_iPass );
if( m_bPalette )
{
PNGTRACE1((_T("Palette used\n")));
}
if( m_bColor )
{
PNGTRACE1((_T("Color used\n")));
}
if( m_bAlpha )
{
PNGTRACE1((_T("Alpha channel used\n")));
}
hResult = FireGetSurfaceEvent();
if( FAILED( hResult ) )
{
return( hResult );
}
m_iAppend = 0;
return( S_OK );
}
HRESULT CPNGFilter::ProcessPLTE()
{
ULONG iColor;
ULONG iByte;
if( !(m_dwChunksEncountered & CHUNK_IHDR) )
{
PNGTRACE((_T("Missing IHDR\n")));
return( E_FAIL );
}
if( m_dwChunksEncountered & CHUNK_PLTE )
{
PNGTRACE((_T("Multiple PLTE chunks\n")));
return( E_FAIL );
}
if( m_dwChunksEncountered & (CHUNK_POSTPLTE|CHUNK_IDAT|CHUNK_IEND) )
{
PNGTRACE((_T("Invalid PLTE placement\n")));
return( E_FAIL );
}
if( !m_bColor )
{
PNGTRACE(( _T("Palettes not allowed for grayscale images - ignoring\n" )));
return( S_OK );
}
m_dwChunksEncountered |= CHUNK_PLTE;
if( m_pngChunkHeader.nDataLength == 0 )
{
return( E_FAIL );
}
if( m_bPalette )
{
// Image requires a palette
if( m_pngChunkHeader.nDataLength > (1U<<m_pngIHDR.nBitDepth)*3 )
{
return( E_FAIL );
}
}
else
{
if( m_pngChunkHeader.nDataLength > 256*3 )
{
return( E_FAIL );
}
}
if( m_pngChunkHeader.nDataLength%3 != 0 )
{
return( E_FAIL );
}
m_nColors = m_pngChunkHeader.nDataLength/3;
iByte = 0;
for( iColor = 0; iColor < m_nColors; iColor++ )
{
m_argbColors[iColor].rgbRed = m_abData[iByte];
m_argbColors[iColor].rgbGreen = m_abData[iByte+1];
m_argbColors[iColor].rgbBlue = m_abData[iByte+2];
// ATLTRACE( "Palette[%x] = (%x, %x, %x)\n", iColor, m_abData[iByte],
// m_abData[iByte+1], m_abData[iByte+2] );
iByte += 3;
}
m_iAppend = 0;
return( S_OK );
}
HRESULT CPNGFilter::ProcessTRNS()
{
WORD *pw = (WORD *)m_abData;
RGBQUAD trans;
int byShiftCnt;
ULONG i;
HRESULT hResult;
DDCOLORKEY ddKey;
// TRNS chunk must precede first IDAT chunk and must follow the
// PLTE chunk (if any).
if ((m_dwChunksEncountered & CHUNK_IDAT)
|| (m_bPalette && (~m_dwChunksEncountered & CHUNK_PLTE)))
{
PNGTRACE((_T("Invalid TRNS placement\n")));
return (E_FAIL);
}
m_dwChunksEncountered |= CHUNK_TRNS;
switch (m_pngIHDR.bColorType)
{
case PNG_COLORTYPE_RGB:
case PNG_COLORTYPE_GRAY:
// ISSUE: we really should preserve the full 16-bit values
// for proper transparent calculation but our main client,
// MSHTML, doesn't preserve the RGB values at 16-bit resolution
// either so it doesn't matter.
byShiftCnt = (m_eSrcFormat == SRC_RGB_48) ? 8 : 0;
trans.rgbRed = (BYTE)(my_ntohl(pw[0]) >> byShiftCnt);
trans.rgbReserved = 0;
if (m_pngIHDR.bColorType == PNG_COLORTYPE_GRAY)
{
trans.rgbGreen = trans.rgbBlue = trans.rgbRed;
}
else
{
trans.rgbGreen = (BYTE)(my_ntohl(pw[1]) >> byShiftCnt);
trans.rgbBlue = (BYTE)(my_ntohl(pw[2]) >> byShiftCnt);
}
m_nTransparentColors = 1;
m_dwTransKey = *((DWORD *)&trans);
break;
case PNG_COLORTYPE_INDEXED:
// Fill in m_abTrans. Remember this is filled with
// the identity map in the constructor...
for (i = 0; i < m_pngChunkHeader.nDataLength; ++i)
{
if (m_abData[i] != 0xff)
{
if (m_nTransparentColors++)
{
// collapse transparent index to first level seen
m_abTrans[i] = (BYTE)m_dwTransKey;
}
else
{
// first transparent index seen
m_dwTransKey = i;
m_abTrans[i] = (BYTE)i;
}
}
}
break;
default:
PNGTRACE(( _T("Color type %d doesn't allow tRNS chunk\n"), m_pngIHDR.bColorType ));
return E_FAIL;
}
// Tell the surface what the transparent index is
ddKey.dwColorSpaceLowValue = m_dwTransKey;
ddKey.dwColorSpaceHighValue = m_dwTransKey;
hResult = m_pDDrawSurface->SetColorKey(DDCKEY_SRCBLT, &ddKey);
return (S_OK);
}
HRESULT CPNGFilter::ProcessGAMA()
{
double gbright, gcvideo, file_gamma, max_sample, final_gamma;
ULONG ulGamma;
int i, iGamma;
// GAMA chunk must precede first IDAT chunk
if (m_dwChunksEncountered & CHUNK_IDAT)
{
PNGTRACE((_T("Invalid GAMA placement\n")));
return (E_FAIL);
}
m_dwChunksEncountered |= CHUNK_GAMA;
// Get the file gamma and compute table if it's not 1.0
ulGamma = my_ntohl(*((ULONG *)m_abData));
max_sample = 255;
// use our precomputed table if possible
if (ulGamma == 100000)
{
memcpy(m_abGamma, gamma10, sizeof(gamma10));
}
else
{
file_gamma = ulGamma / 100000.0;
final_gamma = (VIEWING_GAMMA / (file_gamma * DISPLAY_GAMMA));
for (i = 0; i < 256; ++i)
{
gbright = (double)i / max_sample;
gcvideo = pow(gbright, final_gamma);
iGamma = (int)(gcvideo * MAXFBVAL + 0.5);
m_abGamma[i] = (iGamma > 255) ? (BYTE)255 : (BYTE)iGamma;
}
}
return (S_OK);
}
HRESULT CPNGFilter::ReadChunkCRC()
{
HRESULT hResult;
ULONG nBytesRead;
BYTE* pBuffer;
if( m_nBytesLeftInCurrentTask == 0 )
{
m_nBytesLeftInCurrentTask = 4;
}
pBuffer = LPBYTE( &m_dwChunkCRC )+4-m_nBytesLeftInCurrentTask;
hResult = m_pStream->Read( pBuffer, m_nBytesLeftInCurrentTask,
&nBytesRead );
m_nBytesLeftInCurrentTask -= nBytesRead;
switch( hResult )
{
case S_OK:
break;
case S_FALSE:
return( E_FAIL );
break;
default:
return( hResult );
break;
}
m_dwChunkCRC = my_ntohl( m_dwChunkCRC );
if( m_dwChunkCRC != ~m_dwCRC )
{
PNGTRACE((_T("Bad CRC\n")));
return( E_FAIL );
}
if( m_pngChunkHeader.dwChunkType == PNG_CHUNK_IEND )
{
PNGTRACE1((_T("Finished IEND chunk\n")));
}
return( S_OK );
}
HRESULT CPNGFilter::ReadChunkData()
{
HRESULT hResult = S_OK;
ULONG nBytesToRead;
ULONG nBytesRead;
BYTE* pBuffer;
if( m_nBytesLeftInCurrentTask == 0 )
{
if( m_pngChunkHeader.nDataLength == 0 )
{
return( S_OK );
}
m_iAppend = 0;
m_nDataBytesRead = 0;
m_nBytesLeftInCurrentTask = m_pngChunkHeader.nDataLength;
}
if (m_nBytesLeftInCurrentTask > PNG_BUFFER_SIZE - m_iAppend)
{
// We should have already previously decided to skip too-long data
_ASSERTE(m_bSkipData);
m_bSkipData = TRUE;
}
while (m_nBytesLeftInCurrentTask && hResult == S_OK)
{
pBuffer = &m_abData[m_iAppend];
_ASSERTE(!m_nBytesLeftInCurrentTask || m_iAppend < PNG_BUFFER_SIZE);
nBytesToRead = min(PNG_BUFFER_SIZE - m_iAppend, m_nBytesLeftInCurrentTask);
hResult = m_pStream->Read( pBuffer, nBytesToRead,
&nBytesRead );
m_nBytesLeftInCurrentTask -= nBytesRead;
m_nDataBytesRead += nBytesRead;
m_iAppend += nBytesRead;
m_dwCRC = UpdateCRC( m_dwCRC, pBuffer, nBytesRead );
// If we're just skipping data, reset starting point
if (m_bSkipData)
m_iAppend = 0;
}
switch( hResult )
{
case S_OK:
break;
case S_FALSE:
return( E_FAIL );
break;
default:
return( hResult );
break;
}
return( S_OK );
}
const PNG_INTERLACE_INFO CPNGFilter::s_aInterlaceInfoNone[1] =
{
{
1, 1, 1, 1, 0, 0,
{ 0, 1, 2, 3, 4, 5, 6, 7 },
{ 0, 1, 2, 3, 4, 5, 6, 7 }
}
};
const PNG_INTERLACE_INFO CPNGFilter::s_aInterlaceInfoAdam7[7] =
{
{
8, 8, 8, 8, 0, 0,
{ 0, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 1, 1, 1, 1, 1, 1, 1 }
},
{
8, 8, 4, 8, 4, 0,
{ 0, 0, 0, 0, 0, 1, 1, 1 },
{ 0, 1, 1, 1, 1, 1, 1, 1 }
},
{
4, 8, 4, 4, 0, 4,
{ 0, 1, 1, 1, 1, 2, 2, 2 },
{ 0, 0, 0, 0, 0, 1, 1, 1 }
},
{
4, 4, 2, 4, 2, 0,
{ 0, 0, 0, 1, 1, 1, 1, 2 },
{ 0, 1, 1, 1, 1, 2, 2, 2 }
},
{
2, 4, 2, 2, 0, 2,
{ 0, 1, 1, 2, 2, 3, 3, 4 },
{ 0, 0, 0, 1, 1, 1, 1, 2 }
},
{
2, 2, 1, 2, 1, 0,
{ 0, 0, 1, 1, 2, 2, 3, 3 },
{ 0, 1, 1, 2, 2, 3, 3, 4 }
},
{
1, 2, 1, 1, 0, 1,
{ 0, 1, 2, 3, 4, 5, 6, 7 },
{ 0, 0, 1, 1, 2, 2, 3, 3 }
}
};
BOOL CPNGFilter::BeginPass( ULONG iPass )
{
const PNG_INTERLACE_INFO* pInfo;
ULONG iRightEdgeOfLastPixel;
_ASSERTE( iPass < m_nPasses );
pInfo = &m_pInterlaceInfo[iPass];
m_nDeltaX = pInfo->nDeltaX;
m_nDeltaY = pInfo->nDeltaY;
m_iFirstX = pInfo->iFirstX;
m_iScanLine = pInfo->iFirstY;
m_nPixelsInScanLine = ((m_pngIHDR.nWidth/8)*(8/m_nDeltaX))+
pInfo->anPixelsInPartialBlock[m_pngIHDR.nWidth%8];
m_nBytesInScanLine = (m_nBitsPerPixel*m_nPixelsInScanLine+7)/8;
m_nScanLinesInPass = ((m_pngIHDR.nHeight/8)*(8/m_nDeltaY))+
pInfo->anScanLinesInPartialBlock[m_pngIHDR.nHeight%8];
m_iScanLineInPass = 0;
m_iFirstStaleScanLine = 0;
if( m_bExpandPixels )
{
m_nPixelWidth = pInfo->nPixelWidth;
m_nPixelHeight = pInfo->nPixelHeight;
iRightEdgeOfLastPixel = m_iFirstX+((m_nPixelsInScanLine-1)*m_nDeltaX)+
m_nPixelWidth;
if( iRightEdgeOfLastPixel > m_pngIHDR.nWidth )
{
// The last pixel in the scan line is a partial pixel
m_nPartialPixelWidth = m_nPixelWidth-(iRightEdgeOfLastPixel-
m_pngIHDR.nWidth);
m_nFullPixelsInScanLine = m_nPixelsInScanLine-1;
}
else
{
m_nPartialPixelWidth = 0;
m_nFullPixelsInScanLine = m_nPixelsInScanLine;
}
}
else
{
m_nPixelWidth = 1;
m_nPixelHeight = 1;
m_nPartialPixelWidth = 0;
}
PNGTRACE1((_T("Pass %d. %d pixels in scan line\n"), iPass,
m_nPixelsInScanLine ));
m_zlibStream.next_out = m_pbScanLine;
m_zlibStream.avail_out = m_nBytesInScanLine+1;
if( (m_nPixelsInScanLine == 0) || (m_nScanLinesInPass == 0) )
{
return( TRUE );
}
return( FALSE );
}
HRESULT CPNGFilter::NextPass()
{
BOOL bEmpty;
bEmpty = FALSE;
do
{
m_iPass++;
if( m_iPass < m_nPasses )
{
bEmpty = BeginPass( m_iPass );
}
} while( (m_iPass < m_nPasses) && bEmpty );
if( m_iPass >= m_nPasses )
{
return( S_FALSE );
}
return( S_OK );
}
HRESULT CPNGFilter::NextScanLine()
{
HRESULT hResult;
BYTE* pbTemp;
_ASSERTE( m_zlibStream.avail_out == 0 );
m_iScanLine += m_nDeltaY;
m_iScanLineInPass++;
if( m_iScanLineInPass >= m_nScanLinesInPass )
{
// We're done with this pass
hResult = FireOnProgressEvent();
if( FAILED( hResult ) )
{
return( hResult );
}
hResult = NextPass();
return( hResult );
}
else if( ((m_iScanLine-m_iFirstStaleScanLine)/m_nDeltaY) >= 16 )
{
hResult = FireOnProgressEvent();
if( FAILED( hResult ) )
{
return( hResult );
}
}
pbTemp = m_pbScanLine;
m_pbScanLine = m_pbPrevScanLine;
m_pbPrevScanLine = pbTemp;
m_zlibStream.avail_out = m_nBytesInScanLine+1;
m_zlibStream.next_out = m_pbScanLine;
return( S_OK );
}
HRESULT CPNGFilter::ReadIDATData()
{
HRESULT hResult;
ULONG nBytesToRead;
ULONG nBytesRead;
int nError;
if( !(m_dwChunksEncountered & CHUNK_IHDR) )
{
PNGTRACE((_T("Missing IHDR\n")));
return( E_FAIL );
}
if( m_dwChunksEncountered & CHUNK_LASTIDAT )
{
PNGTRACE((_T("Extra IDAT chunk\n")));
return( E_FAIL );
}
if( !(m_dwChunksEncountered & CHUNK_IDAT) )
{
// This is the first IDAT chunk. Initialize the surface.
hResult = BeginImage();
if( FAILED( hResult ) )
{
return( hResult );
}
}
m_dwChunksEncountered |= CHUNK_IDAT;
nBytesToRead = min( m_pngChunkHeader.nDataLength-m_nDataBytesRead,
PNG_BUFFER_SIZE );
hResult = m_pStream->Read( m_abData, nBytesToRead, &nBytesRead );
m_nDataBytesRead += nBytesRead;
m_dwCRC = UpdateCRC( m_dwCRC, m_abData, nBytesRead );
switch( hResult )
{
case S_OK:
break;
case S_FALSE:
return( E_FAIL );
break;
case E_PENDING:
if( nBytesRead == 0 )
{
return( E_PENDING );
}
break;
default:
return( hResult );
break;
}
m_zlibStream.next_in = m_abData;
m_zlibStream.avail_in = nBytesRead;
do
{
nError = inflate( &m_zlibStream, Z_PARTIAL_FLUSH );
if( (nError == Z_OK) || (nError == Z_STREAM_END) )
{
if( m_zlibStream.avail_out == 0 )
{
switch( m_pbScanLine[0] )
{
case 0:
NoneFilterScanLine();
break;
case 1:
SubFilterScanLine();
break;
case 2:
UpFilterScanLine();
break;
case 3:
AverageFilterScanLine();
break;
case 4:
PaethFilterScanLine();
break;
default:
_ASSERT( FALSE );
break;
}
hResult = WriteScanLine();
if( FAILED( hResult ) )
{
return( hResult );
}
hResult = NextScanLine();
if( FAILED( hResult ) )
{
return( hResult );
}
}
else
{
_ASSERTE( m_zlibStream.avail_in == 0 );
}
}
else
{
return( E_FAIL );
}
if( nError == Z_STREAM_END )
{
if( m_nDataBytesRead < m_pngChunkHeader.nDataLength )
{
PNGTRACE((_T("Extra IDAT data\n")));
return( E_FAIL );
}
m_dwChunksEncountered |= CHUNK_LASTIDAT;
m_bFinishedIDAT = TRUE;
inflateEnd( &m_zlibStream );
if( m_dwEvents & IMGDECODE_EVENT_BITSCOMPLETE )
{
hResult = m_pEventSink->OnBitsComplete();
if( FAILED( hResult ) )
{
return( hResult );
}
}
}
} while( (nError == Z_OK) && (m_zlibStream.avail_in > 0) );
return( S_OK );
}
HRESULT CPNGFilter::ReadChunkHeader()
{
HRESULT hResult;
ULONG nBytesRead;
BYTE* pBuffer;
if( m_nBytesLeftInCurrentTask == 0 )
{
m_nBytesLeftInCurrentTask = sizeof( m_pngChunkHeader );
}
pBuffer = LPBYTE( &m_pngChunkHeader )+sizeof( m_pngChunkHeader )-
m_nBytesLeftInCurrentTask;
hResult = m_pStream->Read( pBuffer, m_nBytesLeftInCurrentTask,
&nBytesRead );
m_nBytesLeftInCurrentTask -= nBytesRead;
switch( hResult )
{
case S_OK:
break;
case S_FALSE:
return( E_FAIL );
break;
default:
return( hResult );
break;
}
FixByteOrder( &m_pngChunkHeader );
m_dwCRC = UpdateCRC( 0xffffffff, LPBYTE( &m_pngChunkHeader.dwChunkType ),
sizeof( m_pngChunkHeader.dwChunkType ) );
#ifdef BIG_ENDIAN
m_pngChunkHeader.dwChunkType = endianConverter(m_pngChunkHeader.dwChunkType);
#endif
m_nDataBytesRead = 0;
m_bSkipData = FALSE;
PNGTRACE1((_T("Chunk type: %c%c%c%c\n"), m_pngChunkHeader.dwChunkType&0xff,
(m_pngChunkHeader.dwChunkType>>8)&0xff,
(m_pngChunkHeader.dwChunkType>>16)&0xff,
(m_pngChunkHeader.dwChunkType>>24)&0xff ));
PNGTRACE1((_T("Data length: %d\n"), m_pngChunkHeader.nDataLength ));
if( !(m_pngChunkHeader.dwChunkType & PNG_CHUNK_ANCILLARY) )
{
switch( m_pngChunkHeader.dwChunkType )
{
case PNG_CHUNK_IHDR:
case PNG_CHUNK_PLTE:
case PNG_CHUNK_IEND:
// If m_pngChunkHeader.nDataLength > 4096 on an critical non-IDAT chunk,
// we can't decode it, so fail.
if (m_pngChunkHeader.nDataLength > PNG_BUFFER_SIZE)
{
PNGTRACE((_T("Critical chunk too long\n")));
return( E_FAIL );
}
break;
case PNG_CHUNK_IDAT:
break;
default:
PNGTRACE((_T("Unknown critical chunk\n")));
return( E_FAIL );
break;
}
}
else
{
// If m_pngChunkHeader.nDataLength > 4096 on an ancillary chunk,
// set a flag so we discard the data
if (m_pngChunkHeader.nDataLength > PNG_BUFFER_SIZE)
{
PNGTRACE((_T("Discarding ancillary chunk that is too long\n")));
m_bSkipData = TRUE;
}
}
return( S_OK );
}
static const BYTE g_abPNGHeader[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
HRESULT CPNGFilter::ReadFileHeader()
{
HRESULT hResult;
ULONG nBytesRead;
BYTE* pBuffer;
if( m_nBytesLeftInCurrentTask == 0 )
{
m_nBytesLeftInCurrentTask = 8;
}
pBuffer = &m_abData[m_iAppend];
hResult = m_pStream->Read( pBuffer, m_nBytesLeftInCurrentTask,
&nBytesRead );
m_nBytesLeftInCurrentTask -= nBytesRead;
switch( hResult )
{
case S_OK:
break;
case S_FALSE:
return( E_FAIL );
break;
default:
return( hResult );
break;
}
if( memcmp( m_abData, g_abPNGHeader, 8 ) == 0 )
{
PNGTRACE1((_T("File is a PNG image\n")));
}
else
{
PNGTRACE((_T("File is not a PNG image\n")));
return( E_FAIL );
}
m_iAppend = 0;
return( S_OK );
}
HRESULT CPNGFilter::EatData()
{
m_iAppend = 0;
return( S_OK );
}
///////////////////////////////////////////////////////////////////////////////
// IImageDecodeFilter methods
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPNGFilter::Initialize( IImageDecodeEventSink* pEventSink )
{
HRESULT hResult;
if( pEventSink == NULL )
{
return( E_INVALIDARG );
}
m_pEventSink = pEventSink;
hResult = m_pEventSink->OnBeginDecode( &m_dwEvents, &m_nFormats,
&m_pFormats );
if( FAILED( hResult ) )
{
return( hResult );
}
return( S_OK );
}
STDMETHODIMP CPNGFilter::Process( IStream* pStream )
{
HRESULT hResult;
BYTE bData;
ULONG nBytesRead;
// We have to do this every time. We don't AddRef, since we don't hold onto
// the stream.
m_pStream = pStream;
do
{
switch( m_eInternalState )
{
case ISTATE_READFILEHEADER:
hResult = ReadFileHeader();
break;
case ISTATE_READCHUNKHEADER:
hResult = ReadChunkHeader();
break;
case ISTATE_READCHUNKDATA:
hResult = ReadChunkData();
break;
case ISTATE_READIDATDATA:
hResult = ReadIDATData();
break;
case ISTATE_READCHUNKCRC:
hResult = ReadChunkCRC();
break;
case ISTATE_EATDATA:
hResult = EatData();
break;
case ISTATE_PROCESSBKGD:
hResult = ProcessBKGD();
break;
case ISTATE_CHOOSEBKGD:
hResult = ChooseBKGD();
break;
case ISTATE_PROCESSTRNS:
hResult = ProcessTRNS();
break;
case ISTATE_PROCESSGAMA:
hResult = ProcessGAMA();
break;
case ISTATE_PROCESSIEND:
hResult = ProcessIEND();
break;
case ISTATE_PROCESSIHDR:
hResult = ProcessIHDR();
break;
case ISTATE_PROCESSPLTE:
hResult = ProcessPLTE();
break;
case ISTATE_DONE:
hResult = m_pStream->Read( &bData, 1, &nBytesRead );
if (hResult == S_OK && nBytesRead == 0)
hResult = S_FALSE;
break;
default:
PNGTRACE((_T("Unknown state\n")));
_ASSERT( FALSE );
hResult = E_UNEXPECTED;
break;
}
if( hResult == S_OK )
{
NextState();
}
} while( hResult == S_OK );
m_pStream = NULL;
return( hResult );
}
STDMETHODIMP CPNGFilter::Terminate( HRESULT hrStatus )
{
PNGTRACE1((_T("Image decode terminated. Status: %x\n"), hrStatus ));
if (m_pDDrawSurface != NULL)
{
m_pDDrawSurface.Release();
}
if( m_pEventSink != NULL )
{
m_pEventSink->OnDecodeComplete( hrStatus );
m_pEventSink.Release();
}
return( S_OK );
}
HRESULT CPNGFilter::WriteScanLine()
{
ULONG nPixelHeight;
ULONG iScanLine;
RECT rect;
HRESULT hResult;
void* pBits = NULL;
LONG nPitch;
nPixelHeight = min( m_nPixelHeight, m_pngIHDR.nHeight-m_iScanLine );
if (nPixelHeight < 1)
return S_OK;
rect.left = m_iFirstX;
rect.top = m_iScanLine;
rect.right = m_pngIHDR.nWidth;
rect.bottom = m_iScanLine+nPixelHeight;
hResult = LockBits( &rect, SURFACE_LOCK_EXCLUSIVE, &pBits,
&nPitch );
if( FAILED( hResult ) )
{
return( hResult );
}
m_pfnCopyScanLine( pBits, &m_pbScanLine[1], m_nPixelsInScanLine, m_nDeltaX,
&m_frgbBackground, m_bPalette ? m_abTrans : m_abGamma);
if( m_bExpandPixels )
{
for( iScanLine = 0; iScanLine < nPixelHeight; iScanLine++ )
{
m_pfnDuplicateScanLine( pBits, m_nDeltaX, m_nFullPixelsInScanLine,
m_nPixelWidth, m_nPartialPixelWidth );
}
}
UnlockBits( &rect, pBits );
return( S_OK );
}