Windows2000/private/shell/ext/thumbvw/bmp.cpp
2020-09-30 17:12:32 +02:00

556 lines
16 KiB
C++

#include "precomp.h"
#include <IImgCtx.h>
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
CHAR const c_szGraphicsFilters[] = "Software\\Microsoft\\Shared Tools\\Graphics Filters\\Import";
CHAR const c_szExts[] = "Extensions";
CHAR const c_szShowProgressDialog[] = "ShowProgressDialog";
CHAR const c_szShowOptionsDialog[] = "ShowOptionsDialog";
CHAR const c_szPath[] = "Path";
CHAR const c_szYes[] = "Yes";
CHAR const c_szNo[] = "No";
// Register the classes.
UINT FormatMessageBox( HWND hwnd, UINT idMsg, UINT idTitle,
LPVOID * pArgsMsg, LPVOID * pArgsTitle,
UINT idIconResource, UINT uFlags );
BOOL CheckStringForExtension( LPSTR pszString, LPCSTR pszExt );
#define MAX( a, b ) ( a > b ? a : b )
typedef UINT ( FAR PASCAL *LPFILTERINFO )( short v, LPSTR szFilterExten,
HANDLE FAR * fph1, HANDLE FAR * fph2 );
typedef UINT ( FAR PASCAL *LPIMPORTFUNC )( HDC hdc, FILESPEC FAR *lpfs,
GRPI FAR *p, HANDLE hPref );
typedef int ( FAR PASCAL *LPSETFILTERPREF )( HANDLE hPrefMem, LPSTR szOption, LPVOID pvValue,
ULONG dwSize, ULONG dwType );
BOOL HasGraphicsFilter( LPCWSTR pszExt, LPSTR szHandler, DWORD * pcbSize )
{
CHAR szExt[MAX_PATH];
BOOL fRet = FALSE;
HKEY hkFilters = NULL;
int iCtr = 0;
// make sure we have a file extension.
if ( pszExt == NULL ) return FALSE;
// remove the leading dot from file extension, because graphics filters are not
// registered with the dot for extensions.
pszExt = CharNextWrapW( pszExt );
SHUnicodeToAnsi( pszExt, szExt, ARRAYSIZE( szExt ));
// open the key where graphics filters are registered.
LRESULT lRes = RegOpenKeyExA( HKEY_LOCAL_MACHINE, c_szGraphicsFilters, NULL, KEY_READ, &hkFilters );
if ( lRes == ERROR_SUCCESS )
{
CHAR szBuffer[MAX_PATH];
// enumerate through all of the registered filters.
do
{
DWORD dwSize = MAX_PATH;
DWORD dwType = REG_SZ;
lRes = RegEnumKeyExA( hkFilters, iCtr ++, szBuffer, &dwSize, NULL, NULL, NULL, NULL );
if ( lRes != ERROR_SUCCESS )
{
break;
}
HKEY hkType = NULL;
lRes = RegOpenKeyExA( hkFilters, szBuffer, NULL, KEY_READ, &hkType );
if ( lRes == ERROR_SUCCESS )
{
dwSize = MAX_PATH;
// check their file extension agains the one passed in.
lRes = RegQueryValueExA( hkType, c_szExts, NULL, &dwType, ( LPBYTE )szBuffer, &dwSize );
if ( lRes == ERROR_SUCCESS )
{
fRet = CheckStringForExtension( szBuffer, szExt );
if ( fRet && szHandler )
{
RegQueryValueExA( hkType, c_szPath, NULL, NULL, ( LPBYTE )szHandler, pcbSize );
}
}
RegCloseKey( hkType );
}
}
while ( !fRet );
}
RegCloseKey( hkFilters );
return fRet;
}
BOOL CheckStringForExtension( LPSTR pszString, LPCSTR pszExt )
{
LPSTR pszTmp = pszString;
do
{
LPSTR pszTok = StrStrIA( pszTmp, pszExt );
if ( pszTok )
{
// check that we don't have a partial
int iEndPos = lstrlenA( pszExt );
if ( pszTmp[iEndPos] == CHAR('\0') || pszTmp[iEndPos] == CHAR('\t')
|| pszTmp[iEndPos] == CHAR(','))
{
// we found the token ...
return TRUE;
}
// skip this token.
pszTok = pszTok + iEndPos;
}
pszTmp = pszTok;
}
while ( pszTmp );
return FALSE;
}
COfficeThumb::COfficeThumb( )
{
m_szPath[0] = 0;
}
COfficeThumb::~COfficeThumb()
{
}
STDMETHODIMP COfficeThumb::GetLocation ( LPWSTR pszPathBuffer,
DWORD cch,
DWORD * pdwPriority,
const SIZE * prgSize,
DWORD dwRecClrDepth,
DWORD *pdwFlags )
{
if ( !pdwFlags || !pszPathBuffer || !prgSize )
{
return E_INVALIDARG;
}
m_rgSize = * prgSize;
m_dwRecClrDepth = dwRecClrDepth;
HRESULT hr = NOERROR;
if ( *pdwFlags & IEIFLAG_ASYNC )
{
if ( !pdwPriority )
{
return E_INVALIDARG;
}
hr = E_PENDING;
// lower than normal priority
*pdwPriority = 0x01000000;
}
m_fOrigSize = BOOLIFY( *pdwFlags & IEIFLAG_ORIGSIZE );
*pdwFlags = IEIFLAG_CACHE;
StrCpyNW( pszPathBuffer, m_szPath, cch );
return hr;
}
STDMETHODIMP COfficeThumb::Extract ( HBITMAP * phBmpThumbnail)
{
HMODULE hModule = NULL;
FILESPEC fileSpec; // file to load
GRPI pict;
UINT rc; // return code
HANDLE hPrefMem = NULL; // filter-supplied preferences
UINT wFilterType; // 2 = graphics filter
CHAR szHandler[MAX_PATH];
LPBITMAPINFOHEADER lpbi = NULL;
LPBITMAPINFO lpbInfo = NULL;
LPVOID lpBits = NULL;
WORD nNumColors;
HRESULT hr = E_FAIL;
WORD nVersion = 3;
DWORD cbSize = MAX_PATH;
LPFILTERINFO GetFilterInfo = NULL;
LPIMPORTFUNC ImportGR = NULL;
LPSETFILTERPREF SetFilterPref = NULL;
DWORD dwErr = 0L;
LPCWSTR pszExt = PathFindExtensionW( m_szPath );
if ( !pszExt )
{
return E_INVALIDARG;
}
if ( !HasGraphicsFilter( pszExt, szHandler, & cbSize ))
{
/*[TODO: report error ]*/
return E_UNEXPECTED;
}
Assert (szHandler[0] != 0);
hModule = LoadLibraryA( szHandler );
if ( hModule == NULL )
{
/*[TODO: report error ]*/
return E_UNEXPECTED;
}
// get a pointer to the ImportGR function.
ImportGR = ( LPIMPORTFUNC )GetProcAddress( hModule, "ImportGr" );
if ( ImportGR == NULL )
{
// try the other name (they stupidly exported some of the functions with the
// wrong capitalisation)
ImportGR = ( LPIMPORTFUNC )GetProcAddress( hModule, "ImportGR" );
}
GetFilterInfo = ( LPFILTERINFO )GetProcAddress( hModule, "GetFilterInfo" );
SetFilterPref = ( LPSETFILTERPREF )GetProcAddress( hModule, "SetFilterPref" );
if ( ( GetFilterInfo == NULL ) || ( ImportGR == NULL ) )
{
GetFilterInfo = ( LPFILTERINFO )GetProcAddress( hModule, "GetFilterInfo@16" );
ImportGR = ( LPIMPORTFUNC )GetProcAddress( hModule, "ImportGr@16" );
SetFilterPref = NULL;
nVersion = 2;
}
if ( ImportGR == NULL || GetFilterInfo == NULL )
{
/*[TODO: report error ]*/
FreeLibrary( hModule );
return E_UNEXPECTED;
}
wFilterType = ( *GetFilterInfo )(( short ) 2, // interface version no.
LPSTR(""), // end of .INI entry
( HANDLE FAR * ) &hPrefMem, // fill in: preferences
( HANDLE FAR * ) NULL ); // unused in Windows
/* the return value is the type of filter: 0=error,
* 1=text-filter, 2=graphics-filter
*/
if ( wFilterType == 2 )
{
fileSpec.slippery = FALSE; // TRUE if file may disappear
fileSpec.write = FALSE; // TRUE if open for write
fileSpec.unnamed = FALSE; // TRUE if unnamed
fileSpec.linked = FALSE; // Linked to an FS FCB
fileSpec.mark = FALSE; // Generic mark bit
fileSpec.handle = 0; // MS-DOS open file handle
fileSpec.filePos = 0L;
SHUnicodeToAnsi( m_szPath, fileSpec.fullName, ARRAYSIZE( fileSpec.fullName ));
pict.h = NULL;
pict.inch = 0;
// if something throws an exception in the filter grab it and show an error.
__try
{
// turn off the progress bar if possible (version 3 feature).
if ( SetFilterPref )
{
// LOC: the use of strlen here is fine because both the strings are
// LOC: compiled into the code page and are ANSI
SetFilterPref( hPrefMem, ( LPSTR )c_szShowProgressDialog, ( LPSTR )c_szNo,
strlen( c_szNo ), REG_SZ );
SetFilterPref( hPrefMem, ( LPSTR )c_szShowOptionsDialog, ( LPSTR )c_szNo,
strlen( c_szNo ), REG_SZ );
}
rc = ( *ImportGR ) ( NULL, // "the target DC" (printer?)
( FILESPEC FAR * ) &fileSpec, // file to read
( GRPI FAR * ) &pict, // fill in: result metafile
( HANDLE ) hPrefMem ); // preferences memory
// turn progress bar back on when we're done.
if ( SetFilterPref )
{
SetFilterPref( hPrefMem, ( LPSTR )c_szShowProgressDialog, ( LPSTR )c_szYes,
lstrlenA( c_szYes ) + 1, REG_SZ );
}
if ( rc != 0 || pict.h == NULL )
{
/*[TODO: error report ]*/
FreeLibrary( hModule );
goto ErrorCleanup;
}
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
/*[TODO: report error ]*/
goto ErrorCleanup;
}
if ( nVersion < 3 )
{
// find the BITMAPINFO in the returned metafile
// this saves us from creating a metafile and duplicating
// all the memory.
LPMETAHEADER lphMeta = NULL;
lphMeta = ( LPMETAHEADER )GlobalLock( pict.h );
lpbi = MetaHeaderToBitmapInfo( lphMeta );
if ( !lpbi )
{
GlobalUnlock( pict.h );
}
}
else
{
// this is a HMETAFILE (not a pointer to the metafile struct),
// so it must be handled differently.
lpbi = HMetafileToBitmapInfo( pict );
DeleteMetaFile(( HMETAFILE ) pict.h );
pict.h = NULL;
}
if ( lpbi != NULL )
{
if ( !( nNumColors = ( WORD ) lpbi->biClrUsed ))
{
// no color table for 24-bit, default size otherwise
if ( lpbi->biBitCount != 24 )
{
nNumColors = 1 << lpbi->biBitCount; // standard size table
}
}
// fill in some default values if they are zero.
if ( lpbi->biClrUsed == 0 )
{
lpbi->biClrUsed = ( DWORD )nNumColors;
}
// prepare pointers for conversion call.
lpbInfo = ( LPBITMAPINFO )lpbi;
HPALETTE hpal = NULL;
if ( m_dwRecClrDepth == 8 )
{
hpal = SHCreateShellPalette( NULL );
}
else if ( m_dwRecClrDepth < 8 )
{
hpal = (HPALETTE) GetStockObject( DEFAULT_PALETTE );
}
// converts bitmap into a 120x120 thumbnail and returns it in the pointer.
if ( ConvertDIBSECTIONToThumbnail( lpbInfo, CalcBitsOffsetInDIB( lpbInfo ),
phBmpThumbnail, &m_rgSize, m_dwRecClrDepth, hpal, 15, m_fOrigSize ) == FALSE )
{
/*[TODO: report error ]*/
}
else
{
hr = S_OK;
}
if ( hpal )
{
DeletePalette( hpal );
}
}
}
ErrorCleanup:
if ( pict.h )
{
if ( lpbi )
{
GlobalUnlock( pict.h );
}
// free metafile picture.
GlobalFree( pict.h );
}
else if ( lpbi )
{
// otherwise, if pict.h was a handle, then must release the
// dib that was created from it.
LocalFree( lpbi );
}
if ( hPrefMem != NULL )
{
GlobalFree( hPrefMem );
}
if ( hModule )
{
FreeLibrary( hModule );
}
return hr;
}
LPBITMAPINFOHEADER COfficeThumb::HMetafileToBitmapInfo( GRPI pict )
{
HDC hDC = GetDC( GetDesktopWindow( ) );
HDC hMemDC = CreateCompatibleDC( hDC );
int iWidth = int( ( ( pict.bbox.right - pict.bbox.left ) * 72L ) / pict.inch );
int iHeight = int( ( ( pict.bbox.bottom - pict.bbox.top ) * 72L ) / pict.inch );
LPVOID pBits = NULL;
LPBITMAPINFO pbi = NULL;
BITMAPINFO bi;
if ( ( iWidth > 640 ) || ( iHeight > 640 ) )
{
// don't create too big a bitmap; nothing greater than 640 resolution.
int iTmp = MAX( iWidth, iHeight );
iWidth = ( iWidth * 640 )/ iTmp;
iHeight = ( iHeight * 640 ) / iTmp;
}
ZeroMemory( &bi, sizeof( BITMAPINFO ) );
bi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
bi.bmiHeader.biWidth = iWidth;
bi.bmiHeader.biHeight = iHeight;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 24;
bi.bmiHeader.biCompression = BI_RGB;
DWORD dwBPL = ( ( ( bi.bmiHeader.biWidth * 24 ) + 31 ) >> 3 ) & ~3;
bi.bmiHeader.biSizeImage = dwBPL * bi.bmiHeader.biHeight;
HBITMAP hBmp = CreateDIBSection( hDC, &bi, DIB_RGB_COLORS, &pBits, NULL, NULL );
if ( hBmp == NULL || pBits == NULL )
{
return NULL;
}
HGDIOBJ hOldBmp = SelectObject( hMemDC, hBmp );
// set draw parameters.
SetMapMode( hMemDC, MM_ANISOTROPIC );
SetWindowOrgEx( hMemDC, pict.bbox.left, pict.bbox.top, NULL );
SetWindowExtEx( hMemDC, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, NULL );
SetViewportOrgEx( hMemDC, 0, 0, NULL );
SetViewportExtEx( hMemDC, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, NULL );
// play the metafile.
BOOL bRet = PlayMetaFile( hMemDC, ( HMETAFILE )pict.h );
if ( bRet )
{
GdiFlush( );
hBmp = (HBITMAP) SelectObject( hMemDC, hOldBmp );
pbi = ( LPBITMAPINFO )LocalAlloc( LPTR, bi.bmiHeader.biSize + bi.bmiHeader.biSizeImage );
if ( pbi )
{
CopyMemory( pbi, &bi, bi.bmiHeader.biSize );
LPVOID pTmp = ( LPBYTE )pbi + bi.bmiHeader.biSize;
CopyMemory( pTmp, pBits, bi.bmiHeader.biSizeImage );
}
}
DeleteObject( hBmp );
DeleteDC( hMemDC );
ReleaseDC( GetDesktopWindow( ), hDC );
return ( LPBITMAPINFOHEADER )pbi;
}
// find the DIB bitmap in a memory meta file...
LPBITMAPINFOHEADER COfficeThumb::MetaHeaderToBitmapInfo( LPMETAHEADER pmh )
{
LPMETARECORD pmr;
for ( pmr = ( LPMETARECORD )( ( LPBYTE )pmh + pmh->mtHeaderSize * 2 );
pmr < ( LPMETARECORD )( ( LPBYTE )pmh + pmh->mtSize * 2 );
pmr = ( LPMETARECORD )( ( LPBYTE )pmr + pmr->rdSize * 2 ) )
{
switch ( pmr->rdFunction )
{
case META_DIBBITBLT:
return ( LPBITMAPINFOHEADER )&( pmr->rdParm[8] );
case META_DIBSTRETCHBLT:
return ( LPBITMAPINFOHEADER )&( pmr->rdParm[10] );
case META_STRETCHDIB:
return ( LPBITMAPINFOHEADER )&( pmr->rdParm[11] );
case META_SETDIBTODEV:
return ( LPBITMAPINFOHEADER )&( pmr->rdParm[9] );
}
}
return NULL;
}
STDMETHODIMP COfficeThumb::GetClassID(CLSID *pClassID)
{
return E_NOTIMPL;
}
STDMETHODIMP COfficeThumb::IsDirty()
{
return E_NOTIMPL;
}
STDMETHODIMP COfficeThumb::Load( LPCOLESTR pszFileName, DWORD dwMode)
{
HRESULT hr = E_FAIL;
if ( !pszFileName )
{
return E_INVALIDARG;
}
StrCpyNW( m_szPath, pszFileName, ARRAYSIZE( m_szPath ));
// check if we have a filter ....
LPCWSTR pszExt = PathFindExtensionW( m_szPath );
if ( pszExt != NULL )
{
if ( HasGraphicsFilter( pszExt, NULL, NULL ))
{
hr = NOERROR;
}
}
return hr;
}
STDMETHODIMP COfficeThumb::Save( LPCOLESTR pszFileName, BOOL fRemember)
{
return E_NOTIMPL;
}
STDMETHODIMP COfficeThumb::SaveCompleted( LPCOLESTR pszFileName)
{
return E_NOTIMPL;
}
STDMETHODIMP COfficeThumb::GetCurFile( LPOLESTR *ppszFileName)
{
return E_NOTIMPL;
}