#include "precomp.h" #include "thumbutil.h" #include "strsafe.h" HRESULT GetMediaManagerThumbnail(IPropertyStorage * pPropStg, const SIZE * prgSize, DWORD dwClrDepth, HPALETTE hpal, BOOL fOrigSize, HBITMAP * phBmpThumbnail); HRESULT GetDocFileThumbnail(IPropertyStorage * pPropStg, const SIZE * prgSize, DWORD dwClrDepth, HPALETTE hpal, BOOL fOrigSize, HBITMAP * phBmpThumbnail); // PACKEDMETA struct for DocFile thumbnails. typedef struct { WORD mm; WORD xExt; WORD yExt; WORD dummy; } PACKEDMETA; VOID CalcMetaFileSize(HDC hDC, PACKEDMETA * pMeta, const SIZEL * prgSize, RECT * pRect); CDocFileThumb::CDocFileThumb() { m_pszPath = NULL; } CDocFileThumb::~CDocFileThumb() { LocalFree(m_pszPath); // accepts NULL } STDMETHODIMP CDocFileThumb::GetLocation(LPWSTR pszFileName, DWORD cchMax, DWORD * pdwPriority, const SIZE * prgSize, DWORD dwRecClrDepth, DWORD *pdwFlags) { HRESULT hr = E_UNEXPECTED; if (m_pszPath) { m_rgSize = *prgSize; m_dwRecClrDepth = dwRecClrDepth; // just copy the current path into the buffer as we do not share thumbnails... hr = StringCchCopyW(pszFileName, cchMax, m_pszPath); if (SUCCEEDED(hr)) { if (*pdwFlags & IEIFLAG_ASYNC) { // we support async hr = E_PENDING; *pdwPriority = PRIORITY_EXTRACT_NORMAL; } m_fOrigSize = BOOLIFY(*pdwFlags & IEIFLAG_ORIGSIZE); // we don't want it cached.... *pdwFlags &= ~IEIFLAG_CACHE; } } return hr; } HPALETTE PaletteFromClrDepth(DWORD dwRecClrDepth) { HPALETTE hpal = NULL; if (dwRecClrDepth == 8) { hpal = SHCreateShellPalette(NULL); } else if (dwRecClrDepth < 8) { hpal = (HPALETTE)GetStockObject(DEFAULT_PALETTE); } return hpal; } STDMETHODIMP CDocFileThumb::Extract(HBITMAP * phBmpThumbnail) { if (!m_pszPath) return E_UNEXPECTED; IStorage *pstg; HRESULT hr = StgOpenStorage(m_pszPath, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, NULL, &pstg); if (SUCCEEDED(hr)) { IPropertySetStorage *pPropSetStg; hr = pstg->QueryInterface(IID_PPV_ARG(IPropertySetStorage, &pPropSetStg)); if (SUCCEEDED(hr)) { // "MIC" Microsoft Image Composer files needs special casing because they use // the Media Manager internal thumbnail propertyset ... (by what it would be like // to be standard for once ....) FMTID fmtidPropSet = StrCmpIW(PathFindExtensionW(m_pszPath), L".MIC") ? FMTID_SummaryInformation : FMTID_CmsThumbnailPropertySet; IPropertyStorage *pPropSet; hr = pPropSetStg->Open(fmtidPropSet, STGM_READ | STGM_SHARE_EXCLUSIVE, &pPropSet); if (SUCCEEDED(hr)) { HPALETTE hpal = PaletteFromClrDepth(m_dwRecClrDepth); if (FMTID_CmsThumbnailPropertySet == fmtidPropSet) { hr = GetMediaManagerThumbnail(pPropSet, &m_rgSize, m_dwRecClrDepth, hpal, m_fOrigSize, phBmpThumbnail); } else { hr = GetDocFileThumbnail(pPropSet, &m_rgSize, m_dwRecClrDepth, hpal, m_fOrigSize, phBmpThumbnail); } if (hpal) DeleteObject(hpal); pPropSet->Release(); } pPropSetStg->Release(); } pstg->Release(); } return hr; } STDMETHODIMP CDocFileThumb::Load(LPCOLESTR pszFileName, DWORD dwMode) { LocalFree(m_pszPath); return SHStrDup(pszFileName, &m_pszPath); } HRESULT GetMediaManagerThumbnail(IPropertyStorage * pPropStg, const SIZE * prgSize, DWORD dwClrDepth, HPALETTE hpal, BOOL fOrigSize, HBITMAP * phBmpThumbnail) { // current version of media manager simply stores the DIB data in a under a // named property Thumbnail... // read the thumbnail property from the property storage. PROPVARIANT pvarResult = {0}; PROPSPEC propSpec; propSpec.ulKind = PRSPEC_LPWSTR; propSpec.lpwstr = L"Thumbnail"; HRESULT hr = pPropStg->ReadMultiple(1, &propSpec, &pvarResult); if (SUCCEEDED(hr)) { BITMAPINFO * pbi = (BITMAPINFO *)pvarResult.blob.pBlobData; void *pBits = CalcBitsOffsetInDIB(pbi); hr = E_FAIL; if (pbi->bmiHeader.biSize == sizeof(BITMAPINFOHEADER)) { if (ConvertDIBSECTIONToThumbnail(pbi, pBits, phBmpThumbnail, prgSize, dwClrDepth, hpal, 15, fOrigSize)) { hr = S_OK; } } PropVariantClear(&pvarResult); } return hr; } HRESULT GetDocFileThumbnail(IPropertyStorage * pPropStg, const SIZE * prgSize, DWORD dwClrDepth, HPALETTE hpal, BOOL fOrigSize, HBITMAP * phBmpThumbnail) { HRESULT hr; HDC hDC = GetDC(NULL); HDC hMemDC = CreateCompatibleDC(hDC); if (hMemDC) { HBITMAP hBmp = NULL; PROPSPEC propSpec; PROPVARIANT pvarResult = {0}; // read the thumbnail property from the property storage. propSpec.ulKind = PRSPEC_PROPID; propSpec.propid = PIDSI_THUMBNAIL; hr = pPropStg->ReadMultiple(1, &propSpec, &pvarResult); if (SUCCEEDED(hr)) { // assume something is going to go terribly wrong hr = E_FAIL; // make sure we are dealing with a clipboard format. CLIPDATA if ((pvarResult.vt == VT_CF) && (pvarResult.pclipdata->ulClipFmt == -1)) { LPDWORD pdwCF = (DWORD *)pvarResult.pclipdata->pClipData; LPBYTE pStruct = pvarResult.pclipdata->pClipData + sizeof(DWORD); if (*pdwCF == CF_METAFILEPICT) { SetMapMode(hMemDC, MM_TEXT); // handle thumbnail that is a metafile. PACKEDMETA * pMeta = (PACKEDMETA *)pStruct; LPBYTE pData = pStruct + sizeof(PACKEDMETA); RECT rect; UINT cbSize = pvarResult.pclipdata->cbSize - sizeof(DWORD) - sizeof(pMeta->mm) - sizeof(pMeta->xExt) - sizeof(pMeta->yExt) - sizeof(pMeta->dummy); // save as a metafile. HMETAFILE hMF = SetMetaFileBitsEx(cbSize, pData); if (hMF) { SIZE rgNewSize; // use the mapping mode to calc the current size CalcMetaFileSize(hMemDC, pMeta, prgSize, & rect); CalculateAspectRatio(prgSize, &rect); if (fOrigSize) { // use the aspect rect to refigure the size... rgNewSize.cx = rect.right - rect.left; rgNewSize.cy = rect.bottom - rect.top; prgSize = &rgNewSize; // adjust the rect to be the same as the size (which is the size of the metafile) rect.right -= rect.left; rect.bottom -= rect.top; rect.left = 0; rect.top = 0; } if (CreateSizedDIBSECTION(prgSize, dwClrDepth, hpal, NULL, &hBmp, NULL, NULL)) { HGDIOBJ hOldBmp = SelectObject(hMemDC, hBmp); HGDIOBJ hBrush = GetStockObject(WHITE_BRUSH); HGDIOBJ hOldBrush = SelectObject(hMemDC, hBrush); HGDIOBJ hPen = GetStockObject(WHITE_PEN); HGDIOBJ hOldPen = SelectObject(hMemDC, hPen); Rectangle(hMemDC, 0, 0, prgSize->cx, prgSize->cy); SelectObject(hMemDC, hOldBrush); SelectObject(hMemDC, hOldPen); int iXBorder = 0; int iYBorder = 0; if (rect.left == 0) { iXBorder ++; } if (rect.top == 0) { iYBorder ++; } SetViewportExtEx(hMemDC, rect.right - rect.left - 2 * iXBorder, rect.bottom - rect.top - 2 * iYBorder, NULL); SetViewportOrgEx(hMemDC, rect.left + iXBorder, rect.top + iYBorder, NULL); SetMapMode(hMemDC, pMeta->mm); // play the metafile. BOOL bRet = PlayMetaFile(hMemDC, hMF); if (bRet) { *phBmpThumbnail = hBmp; if (*phBmpThumbnail) { // we got the thumbnail bitmap, return success. hr = S_OK; } } DeleteMetaFile(hMF); SelectObject(hMemDC, hOldBmp); if (FAILED(hr) && hBmp) { DeleteObject(hBmp); } } else { hr = DV_E_CLIPFORMAT; } } } else if (*pdwCF == CF_DIB) { // handle thumbnail that is a bitmap. BITMAPINFO * pDib = (BITMAPINFO *) pStruct; if (pDib->bmiHeader.biSize == sizeof(BITMAPINFOHEADER)) { void *pBits = CalcBitsOffsetInDIB(pDib); if (ConvertDIBSECTIONToThumbnail(pDib, pBits, phBmpThumbnail, prgSize, dwClrDepth, hpal, 15, fOrigSize)) { hr = S_OK; } else { hr = DV_E_CLIPFORMAT; } } } else { hr = DV_E_CLIPFORMAT; } } else { hr = DV_E_CLIPFORMAT; } PropVariantClear(&pvarResult); } DeleteDC(hMemDC); } else { hr = E_OUTOFMEMORY; } ReleaseDC(NULL, hDC); return hr; } VOID CalcMetaFileSize(HDC hDC, PACKEDMETA * prgMeta, const SIZEL * prgSize, RECT * prgRect) { ASSERT(prgMeta && prgRect); prgRect->left = 0; prgRect->top = 0; if (!prgMeta->xExt || !prgMeta->yExt) { // no size, then just use the size rect ... prgRect->right = prgSize->cx; prgRect->bottom = prgSize->cy; } else { // set the mapping mode.... SetMapMode(hDC, prgMeta->mm); if (prgMeta->mm == MM_ISOTROPIC || prgMeta->mm == MM_ANISOTROPIC) { // we must set the ViewPortExtent and the window extent to get the scaling SetWindowExtEx(hDC, prgMeta->xExt, prgMeta->yExt, NULL); SetViewportExtEx(hDC, prgMeta->xExt, prgMeta->yExt, NULL); } POINT pt; pt.x = prgMeta->xExt; pt.y = prgMeta->yExt; // convert to pixels.... LPtoDP(hDC, &pt, 1); prgRect->right = abs(pt.x); prgRect->bottom = abs(pt.y); } } STDMETHODIMP CDocFileThumb::GetClassID(CLSID * pCLSID) { return E_NOTIMPL; } STDMETHODIMP CDocFileThumb::IsDirty() { return S_FALSE; } STDMETHODIMP CDocFileThumb::Save(LPCOLESTR pszFileName, BOOL fRemember) { return E_NOTIMPL; } STDMETHODIMP CDocFileThumb::SaveCompleted(LPCOLESTR pszFileName) { return E_NOTIMPL; } STDMETHODIMP CDocFileThumb::GetCurFile(LPOLESTR * ppszFileName) { return E_NOTIMPL; }