|
|
#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 ); }
|