#include "stdafx.h" #include "resource.h" #include "imgutil.h" #include "cdithtbl.h" #include "dithers.h" #include "cdith8.h" #include "align.h" #include "cddsurf.h" //#include void CopyColorsFromPaletteEntries(RGBQUAD *prgb, const PALETTEENTRY *ppe, UINT uCount); CDitherTable* CDitherToRGB8::s_apTableCache[MAX_DITHERTABLE_CACHE_SIZE]; ULONG CDitherToRGB8::s_nCacheSize; CRITICAL_SECTION CDitherToRGB8::s_csCache; void CDitherToRGB8::InitTableCache() { ULONG iTable; InitializeCriticalSection( &s_csCache ); s_nCacheSize = 0; for( iTable = 0; iTable < MAX_DITHERTABLE_CACHE_SIZE; iTable++ ) { s_apTableCache[iTable] = NULL; } } void CDitherToRGB8::CleanupTableCache() { ULONG iTable; EnterCriticalSection( &s_csCache ); for( iTable = 0; iTable < s_nCacheSize; iTable++ ) { _ASSERTE( s_apTableCache[iTable]->m_nRefCount == 0 ); delete s_apTableCache[iTable]; s_apTableCache[iTable] = NULL; } s_nCacheSize = 0; LeaveCriticalSection( &s_csCache ); DeleteCriticalSection( &s_csCache ); } CDitherToRGB8::CDitherToRGB8() : m_dwEvents( 0 ), m_iScanLine( 0 ), m_bProgressiveDither( FALSE ), m_pErrBuf( NULL ), m_pErrBuf1( NULL ), m_pErrBuf2( NULL ), m_pTable( NULL ), m_pbBits( NULL ), m_hbmDestDib( NULL ) { } CDitherToRGB8::~CDitherToRGB8() { if (m_pTable) m_pTable->m_nRefCount--; if (m_hbmDestDib) DeleteObject(m_hbmDestDib); } STDMETHODIMP CDitherToRGB8::GetSurface( LONG nWidth, LONG nHeight, REFGUID bfid, ULONG nPasses, DWORD dwHints, IUnknown** ppSurface ) { HRESULT hResult; CComPtr< IUnknown > pDestSurface; if (ppSurface != NULL) *ppSurface = NULL; if ((nWidth <= 0) || (nHeight <= 0)) return E_INVALIDARG; if (ppSurface == NULL) return E_POINTER; if (IsEqualGUID(bfid, BFID_RGB_24)) { m_eSrcFormat = RGB24; m_nBitsPerPixel = 24; } else if (IsEqualGUID(bfid, BFID_INDEXED_RGB_8)) { m_eSrcFormat = RGB8; m_nBitsPerPixel = 8; } else return E_NOINTERFACE; m_nWidth = nWidth; m_nHeight = nHeight; if ((dwHints & IMGDECODE_HINT_TOPDOWN) && (dwHints & IMGDECODE_HINT_FULLWIDTH) && (m_dwEvents & IMGDECODE_EVENT_PROGRESS)) m_bProgressiveDither = TRUE; else m_bProgressiveDither = FALSE; m_pErrBuf = new ERRBUF[(m_nWidth+2)*2]; if (m_pErrBuf == NULL) return E_OUTOFMEMORY; m_pErrBuf1 = &m_pErrBuf[1]; m_pErrBuf2 = &m_pErrBuf[m_nWidth+3]; memset(m_pErrBuf, 0, sizeof( ERRBUF )*(m_nWidth+2)*2); hResult = m_pEventSink->GetSurface(m_nWidth, m_nHeight, BFID_INDEXED_RGB_8, nPasses, dwHints, &pDestSurface); if (FAILED(hResult)) return( hResult ); hResult = pDestSurface->QueryInterface(IID_IDirectDrawSurface, (void **)&m_pDestSurface); if (FAILED(hResult)) return hResult; m_hbmDestDib = ImgCreateDib(m_nWidth, -LONG(m_nHeight), FALSE, m_nBitsPerPixel, 0, NULL, &m_pbBits, (int *)&m_nPitch); if (m_hbmDestDib == NULL) return E_OUTOFMEMORY; hResult = CreateDDrawSurfaceOnDIB(m_hbmDestDib, &m_pSurface); if (FAILED(hResult)) return hResult; *ppSurface = (IUnknown *)m_pSurface; (*ppSurface)->AddRef(); return S_OK; } STDMETHODIMP CDitherToRGB8::OnBeginDecode( DWORD* pdwEvents, ULONG* pnFormats, GUID** ppFormats ) { HRESULT hResult; GUID* pFormats; ULONG nFormats; ULONG iFormat; BOOL bFound; if( pdwEvents != NULL ) { *pdwEvents = 0; } if( pnFormats != NULL ) { *pnFormats = 0; } if( ppFormats != NULL ) { *pnFormats = NULL; } if( pdwEvents == NULL ) { return( E_POINTER ); } if( pnFormats == NULL ) { return( E_POINTER ); } if( ppFormats == NULL ) { return( E_POINTER ); } hResult = m_pEventSink->OnBeginDecode( &m_dwEvents, &nFormats, &pFormats ); if( FAILED( hResult ) ) { return( hResult ); } bFound = FALSE; for( iFormat = 0; (iFormat < nFormats) && !bFound; iFormat++ ) { if( IsEqualGUID( pFormats[iFormat], BFID_INDEXED_RGB_8 ) ) { bFound = TRUE; } } CoTaskMemFree( pFormats ); if( !bFound ) { return( E_FAIL ); } *ppFormats = (GUID*)CoTaskMemAlloc( 3*sizeof( GUID ) ); if( *ppFormats == NULL ) { return( E_OUTOFMEMORY ); } *pnFormats = 3; (*ppFormats)[0] = BFID_GRAY_8; (*ppFormats)[1] = BFID_RGB_24; (*ppFormats)[2] = BFID_INDEXED_RGB_8; *pdwEvents = m_dwEvents|IMGDECODE_EVENT_BITSCOMPLETE| IMGDECODE_EVENT_PALETTE; return( S_OK ); } STDMETHODIMP CDitherToRGB8::OnBitsComplete() { HRESULT hResult; hResult = DitherFull(); if( FAILED( hResult ) ) { return( hResult ); } if( m_dwEvents & IMGDECODE_EVENT_BITSCOMPLETE ) { hResult = m_pEventSink->OnBitsComplete(); if( FAILED( hResult ) ) { return( hResult ); } } return( S_OK ); } STDMETHODIMP CDitherToRGB8::OnDecodeComplete( HRESULT hrStatus ) { HRESULT hResult; delete m_pErrBuf; m_pErrBuf = NULL; m_pErrBuf1 = NULL; m_pErrBuf2 = NULL; // Propagate the transparency information if necessary if (m_pSurface && m_pDestSurface) { DDCOLORKEY ddKey; if (SUCCEEDED(m_pSurface->GetColorKey(DDCKEY_SRCBLT, &ddKey))) m_pDestSurface->SetColorKey(DDCKEY_SRCBLT, &ddKey); } if( m_pSurface != NULL ) { m_pSurface.Release(); } if( m_pDestSurface != NULL ) { m_pDestSurface.Release(); } hResult = m_pEventSink->OnDecodeComplete( hrStatus ); m_pEventSink.Release(); if( FAILED( hResult ) ) { return( hResult ); } return( S_OK ); } STDMETHODIMP CDitherToRGB8::OnPalette() { HRESULT hResult; CComPtr< IDirectDrawPalette > pPalette; PALETTEENTRY ape[256]; if (m_eSrcFormat == RGB8) { hResult = m_pSurface->GetPalette(&pPalette); if (FAILED(hResult)) return hResult; hResult = pPalette->GetEntries(0, 0, 256, ape); if (FAILED(hResult)) return hResult; CopyColorsFromPaletteEntries(m_argbSrcColors, ape, 256); } if (m_dwEvents & IMGDECODE_EVENT_PALETTE) { hResult = m_pEventSink->OnPalette(); if (FAILED(hResult)) { return hResult; } } return S_OK; } STDMETHODIMP CDitherToRGB8::OnProgress( RECT* pBounds, BOOL bComplete ) { HRESULT hResult; if( pBounds == NULL ) { return( E_INVALIDARG ); } if( m_bProgressiveDither && bComplete ) { hResult = DitherBand( pBounds ); if( FAILED( hResult ) ) { return( hResult ); } } else { hResult = ConvertBlock( pBounds ); if( FAILED( hResult ) ) { return( hResult ); } } if( m_dwEvents & IMGDECODE_EVENT_PROGRESS ) { hResult = m_pEventSink->OnProgress( pBounds, bComplete ); if( FAILED( hResult ) ) { return( hResult ); } } return( S_OK ); } STDMETHODIMP CDitherToRGB8::SetDestColorTable( ULONG nColors, const RGBQUAD* prgbColors ) { ULONG iTable; HRESULT hResult; if( (nColors == 0) || (nColors > 256) ) { return( E_INVALIDARG ); } if( prgbColors == NULL ) { return( E_INVALIDARG ); } EnterCriticalSection( &s_csCache ); if( m_pTable != NULL ) { // Release whatever table we've got already m_pTable->m_nRefCount--; m_pTable = NULL; } // See if we can find the requested table in the cache for( iTable = 0; (iTable < s_nCacheSize) && (m_pTable == NULL); iTable++ ) { if( s_apTableCache[iTable]->Match( nColors, prgbColors ) ) { m_pTable = s_apTableCache[iTable]; m_pTable->m_nRefCount++; } } if( m_pTable == NULL ) { if( s_nCacheSize < MAX_DITHERTABLE_CACHE_SIZE ) { m_pTable = new CDitherTable; if( m_pTable == NULL ) { LeaveCriticalSection( &s_csCache ); return( E_OUTOFMEMORY ); } hResult = m_pTable->SetColors( nColors, prgbColors ); if( FAILED( hResult ) ) { LeaveCriticalSection( &s_csCache ); m_pTable = NULL; return( hResult ); } // Add a new cache entry m_pTable->m_nRefCount++; s_apTableCache[s_nCacheSize] = m_pTable; s_nCacheSize++; } else { // Find a cache entry to replace. for( iTable = 0; (iTable < s_nCacheSize) && (m_pTable == NULL); iTable++ ) { if( s_apTableCache[iTable]->m_nRefCount == 0 ) { m_pTable = s_apTableCache[iTable]; hResult = m_pTable->SetColors( nColors, prgbColors ); if( FAILED( hResult ) ) { LeaveCriticalSection( &s_csCache ); m_pTable = NULL; return( hResult ); } m_pTable->m_nRefCount++; } } } } _ASSERTE( m_pTable != NULL ); LeaveCriticalSection( &s_csCache ); return( S_OK ); } STDMETHODIMP CDitherToRGB8::SetEventSink( IImageDecodeEventSink* pEventSink ) { if( pEventSink == NULL ) { return( E_INVALIDARG ); } m_pEventSink = pEventSink; return( S_OK ); } HRESULT CDitherToRGB8::ConvertBlock( RECT* pBounds ) { HRESULT hResult; void* pSrcBits; void* pDestBits; LONG nSrcPitch; LONG nDestPitch; DDSURFACEDESC ddsd; _ASSERTE( pBounds->left == 0 ); _ASSERTE( pBounds->right == LONG( m_nWidth ) ); ddsd.dwSize = sizeof(ddsd); hResult = m_pSurface->Lock(pBounds, &ddsd, 0, 0); if (FAILED(hResult)) return hResult; pSrcBits = ddsd.lpSurface; nSrcPitch = ddsd.lPitch; hResult = m_pDestSurface->Lock(pBounds, &ddsd, 0, 0); if (FAILED(hResult)) { m_pSurface->Unlock(pSrcBits); return hResult; } pDestBits = ddsd.lpSurface; nDestPitch = ddsd.lPitch; switch( m_eSrcFormat ) { case RGB24: Convert24to8(LPBYTE(pDestBits), LPBYTE(pSrcBits), nDestPitch, nSrcPitch, m_pTable->m_abInverseMap, pBounds->left, pBounds->right-pBounds->left, pBounds->top, pBounds->bottom-pBounds->top ); break; case RGB8: Convert8to8( LPBYTE( pDestBits ), LPBYTE( pSrcBits ), nDestPitch, nSrcPitch, m_argbSrcColors, m_pTable->m_abInverseMap, pBounds->left, pBounds->right-pBounds->left, pBounds->top, pBounds->bottom-pBounds->top ); break; default: return E_FAIL; break; } m_pDestSurface->Unlock(pDestBits); m_pSurface->Unlock(pSrcBits); return S_OK; } HRESULT CDitherToRGB8::DitherBand( RECT* pBounds ) { HRESULT hResult; void* pSrcBits; void* pDestBits; LONG nSrcPitch; LONG nDestPitch; LONG lDestTrans = -1; LONG lSrcTrans = -1; DDSURFACEDESC ddsd; DDCOLORKEY ddColorKey; _ASSERTE( pBounds->left == 0 ); _ASSERTE( pBounds->right == LONG( m_nWidth ) ); ddsd.dwSize = sizeof(ddsd); hResult = m_pSurface->Lock(pBounds, &ddsd, 0, 0); if (FAILED(hResult)) return hResult; pSrcBits = ddsd.lpSurface; nSrcPitch = ddsd.lPitch; hResult = m_pDestSurface->Lock(pBounds, &ddsd, 0, 0); if (FAILED(hResult)) { m_pSurface->Unlock(pSrcBits); return hResult; } pDestBits = ddsd.lpSurface; nDestPitch = ddsd.lPitch; switch (m_eSrcFormat) { case RGB24: Dith24to8(LPBYTE(pDestBits), LPBYTE(pSrcBits), nDestPitch, nSrcPitch, m_pTable->m_argbColors, m_pTable->m_abInverseMap, m_pErrBuf1, m_pErrBuf2, pBounds->left, pBounds->right-pBounds->left, pBounds->top, pBounds->bottom-pBounds->top); break; case RGB8: if (SUCCEEDED(m_pSurface->GetColorKey(DDCKEY_SRCBLT, &ddColorKey))) lSrcTrans = ddColorKey.dwColorSpaceLowValue; if (SUCCEEDED(m_pDestSurface->GetColorKey(DDCKEY_SRCBLT, &ddColorKey))) lDestTrans = ddColorKey.dwColorSpaceLowValue; // preserve the transparent index if necessary if (lSrcTrans >= 0 && lDestTrans == -1) { lDestTrans = lSrcTrans; } if (lSrcTrans == -1 || lDestTrans == -1) { Dith8to8(LPBYTE(pDestBits), LPBYTE(pSrcBits), nDestPitch, nSrcPitch, m_argbSrcColors, m_pTable->m_argbColors, m_pTable->m_abInverseMap, m_pErrBuf1, m_pErrBuf2, pBounds->left, pBounds->right-pBounds->left, pBounds->top, pBounds->bottom-pBounds->top); } else { Dith8to8t(LPBYTE(pDestBits), LPBYTE(pSrcBits), nDestPitch, nSrcPitch, m_argbSrcColors, m_pTable->m_argbColors, m_pTable->m_abInverseMap, m_pErrBuf1, m_pErrBuf2, pBounds->left, pBounds->right-pBounds->left, pBounds->top, pBounds->bottom-pBounds->top, (BYTE)lDestTrans, (BYTE)lSrcTrans); } break; default: return E_FAIL; } m_pDestSurface->Unlock(pDestBits); m_pSurface->Unlock(pSrcBits); return S_OK; } HRESULT CDitherToRGB8::DitherFull() { HRESULT hResult; RECT rect; rect.left = 0; rect.top = 0; rect.right = m_nWidth; rect.bottom = m_nHeight; hResult = DitherBand(&rect); if (FAILED(hResult)) return hResult; return S_OK; } HRESULT DitherTo8( BYTE * pDestBits, LONG nDestPitch, BYTE * pSrcBits, LONG nSrcPitch, REFGUID bfidSrc, RGBQUAD * prgbDestColors, RGBQUAD * prgbSrcColors, BYTE * pbDestInvMap, LONG x, LONG y, LONG cx, LONG cy, LONG lDestTrans, LONG lSrcTrans) { ERRBUF* m_pErrBuf; ERRBUF* m_pErrBuf1; ERRBUF* m_pErrBuf2; // StartCAPAll(); HRESULT hr = S_OK; m_pErrBuf = new ERRBUF[(cx+2)*2]; if (m_pErrBuf == NULL) { return( E_OUTOFMEMORY ); } m_pErrBuf1 = &m_pErrBuf[1]; m_pErrBuf2 = &m_pErrBuf[cx+3]; memset(m_pErrBuf, 0, sizeof( ERRBUF )*(cx+2)*2); if (bfidSrc == BFID_RGB_24) { Dith24to8( pDestBits, pSrcBits, nDestPitch, nSrcPitch, prgbDestColors, pbDestInvMap, m_pErrBuf1, m_pErrBuf2, x, cx, y, cy ); } else if (bfidSrc == BFID_RGB_8) { if (lDestTrans == -1 || lSrcTrans == -1) { Dith8to8( pDestBits, pSrcBits, nDestPitch, nSrcPitch, prgbSrcColors, prgbDestColors, pbDestInvMap, m_pErrBuf1, m_pErrBuf2, x, cx, y, cy ); } else { Dith8to8t( pDestBits, pSrcBits, nDestPitch, nSrcPitch, prgbSrcColors, prgbDestColors, pbDestInvMap, m_pErrBuf1, m_pErrBuf2, x, cx, y, cy, (BYTE)lDestTrans, (BYTE)lSrcTrans ); } } else { hr = E_FAIL; } delete m_pErrBuf; // StopCAPAll(); return hr; }