#include "precomp.h" #include "tasks.h" #include "prevwnd.h" void DeleteBuffer(Buffer * pBuf) { if (pBuf) { if (pBuf->hbmOld) SelectObject(pBuf->hdc, pBuf->hbmOld); if (pBuf->hPalOld) SelectPalette(pBuf->hdc, pBuf->hPalOld, FALSE); if (pBuf->hbm) DeleteObject(pBuf->hbm); if (pBuf->hPal) DeleteObject(pBuf->hPal); if (pBuf->hdc) DeleteDC(pBuf->hdc); delete pBuf; } } //////////////////////////////////////////////////////////////////////////// // // CDecodeTask // //////////////////////////////////////////////////////////////////////////// CDecodeTask::CDecodeTask() : CRunnableTask(RTF_DEFAULT) { InitializeCriticalSection(&_cs); }; CDecodeTask::~CDecodeTask() { ATOMICRELEASE(_pstrm); ATOMICRELEASE(_pfactory); if (_pszFilename) LocalFree(_pszFilename); ATOMICRELEASE(_pSID); DeleteCriticalSection(&_cs); if (_ppi) delete [] _ppi; } HRESULT CDecodeTask::Create(IStream * pstrm, LPCWSTR pszFilename, UINT iItem, IShellImageDataFactory * pFactory, HWND hwnd, IRunnableTask ** ppTask) { *ppTask = NULL; CDecodeTask * pTask = new CDecodeTask(); if (!pTask) return E_OUTOFMEMORY; HRESULT hr = pTask->_Initialize(pstrm, pszFilename, iItem, pFactory, hwnd); if (SUCCEEDED(hr)) { *ppTask = (IRunnableTask*)pTask; } else { pTask->Release(); } return hr; } HRESULT CDecodeTask::_Initialize(IStream *pstrm, LPCWSTR pszFilename, UINT iItem, IShellImageDataFactory *pFactory, HWND hwnd) { if (pstrm) { STATSTG stat; if (SUCCEEDED(pstrm->Stat(&stat, 0))) { _pszFilename = StrDup(stat.pwcsName); _fIsReadOnly = !(stat.grfMode & STGM_WRITE); CoTaskMemFree(stat.pwcsName); } _pstrm = pstrm; _pstrm->AddRef(); } else { _pszFilename = StrDup(pszFilename); if (!_pszFilename) return E_OUTOFMEMORY; } if (!_pstrm && _pszFilename) { SHGetFileInfo(_pszFilename, 0, &_sfi, sizeof(_sfi), SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES); } else { ZeroMemory(&_sfi, sizeof(_sfi)); } _pfactory = pFactory; _pfactory->AddRef(); _hwndNotify = hwnd; _iItem = iItem; return S_OK; } HRESULT CDecodeTask::RunInitRT() { HRESULT hr; EnterCriticalSection(&_cs); if (_pstrm) { hr = _pfactory->CreateImageFromStream(_pstrm, &_pSID); } else { hr = _pfactory->CreateImageFromFile(_pszFilename, &_pSID); _fIsReadOnly = (GetFileAttributes(_pszFilename) & FILE_ATTRIBUTE_READONLY); } if (SUCCEEDED(hr)) { hr = _pSID->Decode(SHIMGDEC_LOADFULL,0,0); if (SUCCEEDED(hr)) { _pSID->GetPageCount(&_cImages); _ppi = new PageInfo[_cImages]; if (_ppi) { _iCurrent = 0; _fAnimated = (S_OK == _pSID->IsAnimated()); _fEditable = (S_OK == _pSID->IsEditable()); PixelFormat pf; _pSID->GetPixelFormat(&pf); _fExtendedPF = IsExtendedPixelFormat(pf); _pSID->GetRawDataFormat(&_guidFormat); for (ULONG i = 0; i < _cImages; i++) { _pSID->SelectPage(i); // this works for animated and multipage _pSID->GetSize(&_ppi[i].szImage); _pSID->GetResolution(&_ppi[i].xDPI, &_ppi[i].yDPI); if (_fAnimated) _pSID->GetDelay(&_ppi[i].dwDelay); } } else hr = E_OUTOFMEMORY; } if (FAILED(hr)) { ATOMICRELEASE(_pSID); } } LeaveCriticalSection(&_cs); AddRef(); _hr = hr; if (!PostMessage(_hwndNotify, IV_SETIMAGEDATA, (WPARAM)this, NULL)) { Release(); } return S_OK; } BOOL CDecodeTask::GetSize(SIZE * psz) { if (!_ppi) return FALSE; *psz = _ppi[_iCurrent].szImage; return TRUE; } BOOL CDecodeTask::GetResolution(ULONG * px, ULONG * py) { if (!_ppi) return FALSE; *px = _ppi[_iCurrent].xDPI; *py = _ppi[_iCurrent].yDPI; return TRUE; } DWORD CDecodeTask::GetDelay() { if (!_ppi) return 0; return _ppi[_iCurrent].dwDelay; } BOOL CDecodeTask::NextPage() { if (_iCurrent >= _cImages-1) return FALSE; _iCurrent++; return TRUE; } BOOL CDecodeTask::PrevPage() { if (_iCurrent == 0) return FALSE; _iCurrent--; return TRUE; } BOOL CDecodeTask::NextFrame() { EnterCriticalSection(&_cs); HRESULT hr = _pSID->NextFrame(); LeaveCriticalSection(&_cs); if (S_OK==hr) { _iCurrent = (_iCurrent+1) % _cImages; } return (S_OK == hr); } BOOL CDecodeTask::SelectPage(ULONG nPage) { if (nPage >= _cImages) return FALSE; _iCurrent = nPage; return TRUE; } BOOL CDecodeTask::ChangePage(CAnnotationSet& Annotations) { BOOL bResult = FALSE; EnterCriticalSection(&_cs); HRESULT hr = _pSID->SelectPage(_iCurrent); if (SUCCEEDED(hr)) { // If we are moving onto a page that was previously rotated // but not saved then our cached size and resolution will be wrong _pSID->GetSize(&_ppi[_iCurrent].szImage); _pSID->GetResolution(&_ppi[_iCurrent].xDPI, &_ppi[_iCurrent].yDPI); Annotations.SetImageData(_pSID); bResult = TRUE; } LeaveCriticalSection(&_cs); return bResult; } HRESULT CDecodeTask::Rotate(DWORD dwAngle) { HRESULT hr; EnterCriticalSection(&_cs); hr = _pSID->Rotate(dwAngle); if (SUCCEEDED(hr)) { ULONG dwTmp = _ppi[_iCurrent].szImage.cx; _ppi[_iCurrent].szImage.cx = _ppi[_iCurrent].szImage.cy; _ppi[_iCurrent].szImage.cy = dwTmp; if (dwAngle == 90 || dwAngle == 270) { dwTmp = _ppi[_iCurrent].xDPI; _ppi[_iCurrent].xDPI =_ppi[_iCurrent].yDPI; _ppi[_iCurrent].yDPI = dwTmp; } } LeaveCriticalSection(&_cs); return hr; } HRESULT CDecodeTask::Lock(IShellImageData ** ppSID) { if (_pSID) { EnterCriticalSection(&_cs); *ppSID = _pSID; return S_OK; } *ppSID = NULL; return E_FAIL; } HRESULT CDecodeTask::Unlock() { LeaveCriticalSection(&_cs); return S_OK; } BOOL CDecodeTask::DisplayName(LPTSTR psz, UINT cch) { // TODO: Just call the _pSID->DisplayName if (_sfi.szDisplayName[0]) { StrCpyN(psz, _sfi.szDisplayName, cch); return TRUE; } return FALSE; } //////////////////////////////////////////////////////////////////////////// // // CDrawTask // //////////////////////////////////////////////////////////////////////////// CDrawTask::CDrawTask() : CRunnableTask(RTF_SUPPORTKILLSUSPEND) { } CDrawTask::~CDrawTask() { if (_pImgData) _pImgData->Release(); // DeleteBuffer is going to check for NULL anyway DeleteBuffer(_pBuf); } HRESULT CDrawTask::QueryInterface(REFIID riid, LPVOID * ppvObj) { if (riid == IID_IShellImageDataAbort) { *ppvObj = static_cast(this); AddRef(); return S_OK; } return CRunnableTask::QueryInterface(riid, ppvObj); } HRESULT CDrawTask::Create(CDecodeTask * pImageData, COLORREF clr, RECT & rcSrc, RECT & rcDest, HWND hwnd, ULONG uMsg, IRunnableTask ** ppTask) { *ppTask = NULL; CDrawTask * pTask = new CDrawTask(); if (!pTask) return E_OUTOFMEMORY; HRESULT hr = pTask->_Initialize(pImageData, clr, rcSrc, rcDest, hwnd, uMsg); if (SUCCEEDED(hr)) { *ppTask = (IRunnableTask*)pTask; } else { pTask->Release(); } return hr; } HRESULT CDrawTask::_Initialize(CDecodeTask * pImageData, COLORREF clr, RECT & rcSrc, RECT & rcDest, HWND hwnd, ULONG uMsg) { _pImgData = pImageData; _pImgData->AddRef(); _dwPage = _pImgData->_iCurrent; _clrBkgnd = clr; _rcSrc = rcSrc; _hwndNotify = hwnd; _uMsgNotify = uMsg; _pBuf = new Buffer; if (!_pBuf) return E_OUTOFMEMORY; _pBuf->rc = rcDest; _pBuf->hPal = NULL; return S_OK; } typedef RGBQUAD RGBQUAD8[256]; HBITMAP _CreateBitmap(HDC hdcWnd, Buffer *pBuf, SIZE *pSize) { int bpp = GetDeviceCaps(hdcWnd, BITSPIXEL); HBITMAP hbm = NULL; if (8==bpp) { PVOID pvBits = NULL; struct { BITMAPINFOHEADER bmih; RGBQUAD8 rgbquad8; } bmi; bmi.bmih.biSize = sizeof(bmi.bmih); bmi.bmih.biWidth = (int)pSize->cx; bmi.bmih.biHeight = (int)pSize->cy; bmi.bmih.biPlanes = 1; bmi.bmih.biBitCount = 8; bmi.bmih.biCompression = BI_RGB; bmi.bmih.biSizeImage = 0; bmi.bmih.biXPelsPerMeter = 0; bmi.bmih.biYPelsPerMeter = 0; bmi.bmih.biClrUsed = 0; // only used for <= 16bpp bmi.bmih.biClrImportant = 0; // // Use the halftone palette // pBuf->hPal = DllExports::GdipCreateHalftonePalette(); pBuf->hPalOld = SelectPalette(pBuf->hdc, pBuf->hPal, FALSE); BYTE aj[sizeof(PALETTEENTRY) * 256]; LPPALETTEENTRY lppe = (LPPALETTEENTRY) aj; RGBQUAD *prgb = (RGBQUAD *) &bmi.rgbquad8; if (GetPaletteEntries(pBuf->hPal, 0, 256, lppe)) { UINT i; for (i = 0; i < 256; i++) { prgb[i].rgbRed = lppe[i].peRed; prgb[i].rgbGreen = lppe[i].peGreen; prgb[i].rgbBlue = lppe[i].peBlue; prgb[i].rgbReserved = 0; } } hbm = CreateDIBSection(hdcWnd,(BITMAPINFO*)&bmi,DIB_RGB_COLORS,&pvBits,NULL,0); } else { hbm = CreateCompatibleBitmap(hdcWnd,pSize->cx, pSize->cy); } return hbm; } HRESULT CDrawTask::InternalResumeRT() { HRESULT hr = E_FAIL; HDC hdcScreen = GetDC(NULL); if (!_pBuf->hdc) { _pBuf->hdc = CreateCompatibleDC(hdcScreen); } if (_pBuf->hdc) { SIZE sz = {RECTWIDTH(_pBuf->rc), RECTHEIGHT(_pBuf->rc)}; // If we were suspended and resumed, we will already have // a GDI bitmap from last time so don't make another one. if (!_pBuf->hbm) { BITMAP bm = {0}; _pBuf->hbm = _CreateBitmap(hdcScreen, _pBuf, &sz); _pBuf->hbmOld = (HBITMAP)SelectObject(_pBuf->hdc, _pBuf->hbm); } if (_pBuf->hbm) { RECT rc = {0,0,sz.cx, sz.cy}; SetBkColor(_pBuf->hdc, _clrBkgnd); ExtTextOut(_pBuf->hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); IShellImageData * pSID; if (SUCCEEDED(_pImgData->Lock(&pSID))) { pSID->SelectPage(_dwPage); IShellImageDataAbort *pAbortPrev = NULL; pSID->RegisterAbort(this, &pAbortPrev); hr = pSID->Draw(_pBuf->hdc, &rc, &_rcSrc); pSID->RegisterAbort(pAbortPrev, NULL); _pImgData->Unlock(); } } } if (hdcScreen) { ReleaseDC(NULL, hdcScreen); } if (QueryAbort() == S_FALSE) { // We were cancelled or suspended, so don't notify our parent // because we have nothing to show for our efforts. hr = (_lState == IRTIR_TASK_SUSPENDED) ? E_PENDING : E_FAIL; } else { // Ran to completion - clean up and notify main thread UINT iIndex = _pImgData->_iItem; ATOMICRELEASE(_pImgData); if (FAILED(hr)) { DeleteBuffer(_pBuf); _pBuf = NULL; } if (PostMessage(_hwndNotify, _uMsgNotify, (WPARAM)_pBuf, (LPARAM)IntToPtr(iIndex))) { _pBuf = NULL; } hr = S_OK; } return hr; } HRESULT CDrawTask::QueryAbort() { // BUGBUG not rady for prime tome - need to return E_PENDING // if state is SUSPENDED if (WaitForSingleObject(_hDone, 0) == WAIT_OBJECT_0) { return S_FALSE; // Abort } return S_OK; }