Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

651 lines
19 KiB

  1. #include "priv.h"
  2. #include <iimgctx.h>
  3. #include "strsafe.h"
  4. class CImgCtxThumb : public IExtractImage2,
  5. public IRunnableTask,
  6. public IPersistFile
  7. {
  8. public:
  9. CImgCtxThumb();
  10. ~CImgCtxThumb();
  11. STDMETHOD(QueryInterface) (REFIID riid, void **ppvObj);
  12. STDMETHOD_(ULONG, AddRef) (void);
  13. STDMETHOD_(ULONG, Release) (void);
  14. // IExtractImage
  15. STDMETHOD (GetLocation) (LPWSTR pszPathBuffer,
  16. DWORD cch,
  17. DWORD * pdwPriority,
  18. const SIZE * prgSize,
  19. DWORD dwRecClrDepth,
  20. DWORD *pdwFlags);
  21. STDMETHOD (Extract)(HBITMAP * phBmpThumbnail);
  22. STDMETHOD (GetDateStamp) (FILETIME * pftTimeStamp);
  23. // IPersistFile
  24. STDMETHOD (GetClassID)(CLSID *pClassID);
  25. STDMETHOD (IsDirty)();
  26. STDMETHOD (Load)(LPCOLESTR pszFileName, DWORD dwMode);
  27. STDMETHOD (Save)(LPCOLESTR pszFileName, BOOL fRemember);
  28. STDMETHOD (SaveCompleted)(LPCOLESTR pszFileName);
  29. STDMETHOD (GetCurFile)(LPOLESTR *ppszFileName);
  30. STDMETHOD (Run)();
  31. STDMETHOD (Kill)(BOOL fWait);
  32. STDMETHOD (Suspend)();
  33. STDMETHOD (Resume)();
  34. STDMETHOD_(ULONG, IsRunning)();
  35. STDMETHOD (InternalResume)();
  36. protected:
  37. friend void CALLBACK OnImgCtxChange(void * pvImgCtx, void * pv);
  38. void CImgCtxThumb::CalcAspectScaledRect(const SIZE * prgSize,
  39. RECT * pRect);
  40. void CImgCtxThumb::CalculateAspectRatio(const SIZE * prgSize,
  41. RECT * pRect);
  42. long m_cRef;
  43. BITBOOL m_fAsync : 1;
  44. BITBOOL m_fOrigSize : 1;
  45. WCHAR m_szPath[MAX_PATH * 4 + 7];
  46. HANDLE m_hEvent;
  47. SIZE m_rgSize;
  48. DWORD m_dwRecClrDepth;
  49. IImgCtx * m_pImg;
  50. LONG m_lState;
  51. HBITMAP * m_phBmp;
  52. };
  53. STDAPI CImgCtxThumb_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  54. {
  55. *ppunk = NULL;
  56. CImgCtxThumb * pExtract = new CImgCtxThumb();
  57. if (pExtract != NULL)
  58. {
  59. *ppunk = SAFECAST(pExtract, IPersistFile *);
  60. return S_OK;
  61. }
  62. return E_OUTOFMEMORY;
  63. }
  64. CImgCtxThumb::CImgCtxThumb()
  65. {
  66. m_fAsync = FALSE;
  67. StringCchCopyW(m_szPath, ARRAYSIZE(m_szPath), L"file://");
  68. m_cRef = 1;
  69. DllAddRef();
  70. }
  71. CImgCtxThumb::~CImgCtxThumb()
  72. {
  73. ATOMICRELEASE(m_pImg);
  74. if (m_hEvent)
  75. {
  76. CloseHandle(m_hEvent);
  77. }
  78. DllRelease();
  79. }
  80. STDMETHODIMP CImgCtxThumb::QueryInterface(REFIID riid, void **ppvObj)
  81. {
  82. static const QITAB qit[] = {
  83. QITABENTMULTI(CImgCtxThumb, IExtractImage, IExtractImage2),
  84. QITABENT(CImgCtxThumb, IExtractImage2),
  85. QITABENT(CImgCtxThumb, IRunnableTask),
  86. QITABENT(CImgCtxThumb, IPersistFile),
  87. { 0 },
  88. };
  89. return QISearch(this, qit, riid, ppvObj);
  90. }
  91. STDMETHODIMP_(ULONG) CImgCtxThumb::AddRef()
  92. {
  93. return InterlockedIncrement(&m_cRef);
  94. }
  95. STDMETHODIMP_(ULONG) CImgCtxThumb::Release()
  96. {
  97. ASSERT( 0 != m_cRef );
  98. ULONG cRef = InterlockedDecrement(&m_cRef);
  99. if ( 0 == cRef )
  100. {
  101. delete this;
  102. }
  103. return cRef;
  104. }
  105. STDMETHODIMP CImgCtxThumb::GetLocation (LPWSTR pszPathBuffer,
  106. DWORD cch,
  107. DWORD * pdwPriority,
  108. const SIZE * prgSize,
  109. DWORD dwRecClrDepth,
  110. DWORD *pdwFlags)
  111. {
  112. if (!pdwFlags || !pszPathBuffer || !prgSize)
  113. {
  114. return E_INVALIDARG;
  115. }
  116. m_rgSize = *prgSize;
  117. m_dwRecClrDepth = dwRecClrDepth;
  118. HRESULT hr = S_OK;
  119. if (*pdwFlags & IEIFLAG_ASYNC)
  120. {
  121. if (!pdwPriority)
  122. {
  123. return E_INVALIDARG;
  124. }
  125. hr = E_PENDING;
  126. m_fAsync = TRUE;
  127. }
  128. m_fOrigSize = BOOLIFY(*pdwFlags & IEIFLAG_ORIGSIZE);
  129. *pdwFlags = IEIFLAG_CACHE;
  130. hr = PathCreateFromUrlW(m_szPath, pszPathBuffer, &cch, URL_UNESCAPE);
  131. return hr;
  132. }
  133. void CALLBACK OnImgCtxChange(void * pvImgCtx, void * pv)
  134. {
  135. CImgCtxThumb * pThis = (CImgCtxThumb *) pv;
  136. ASSERT(pThis);
  137. ASSERT(pThis->m_hEvent);
  138. // we only asked to know about complete anyway....
  139. SetEvent(pThis->m_hEvent);
  140. }
  141. // This function makes no assumption about whether the thumbnail is square, so
  142. // it calculates the scaling ratio for both dimensions and the uses that as
  143. // the scaling to maintain the aspect ratio.
  144. void CImgCtxThumb::CalcAspectScaledRect(const SIZE * prgSize, RECT * pRect)
  145. {
  146. ASSERT(pRect->left == 0);
  147. ASSERT(pRect->top == 0);
  148. int iWidth = pRect->right;
  149. int iHeight = pRect->bottom;
  150. int iXRatio = (iWidth * 1000) / prgSize->cx;
  151. int iYRatio = (iHeight * 1000) / prgSize->cy;
  152. if (iXRatio > iYRatio)
  153. {
  154. pRect->right = prgSize->cx;
  155. // work out the blank space and split it evenly between the top and the bottom...
  156. int iNewHeight = ((iHeight * 1000) / iXRatio);
  157. if (iNewHeight == 0)
  158. {
  159. iNewHeight = 1;
  160. }
  161. int iRemainder = prgSize->cy - iNewHeight;
  162. pRect->top = iRemainder / 2;
  163. pRect->bottom = iNewHeight + pRect->top;
  164. }
  165. else
  166. {
  167. pRect->bottom = prgSize->cy;
  168. // work out the blank space and split it evenly between the left and the right...
  169. int iNewWidth = ((iWidth * 1000) / iYRatio);
  170. if (iNewWidth == 0)
  171. {
  172. iNewWidth = 1;
  173. }
  174. int iRemainder = prgSize->cx - iNewWidth;
  175. pRect->left = iRemainder / 2;
  176. pRect->right = iNewWidth + pRect->left;
  177. }
  178. }
  179. void CImgCtxThumb::CalculateAspectRatio(const SIZE * prgSize, RECT * pRect)
  180. {
  181. int iHeight = abs(pRect->bottom - pRect->top);
  182. int iWidth = abs(pRect->right - pRect->left);
  183. // check if the initial bitmap is larger than the size of the thumbnail.
  184. if (iWidth > prgSize->cx || iHeight > prgSize->cy)
  185. {
  186. pRect->left = 0;
  187. pRect->top = 0;
  188. pRect->right = iWidth;
  189. pRect->bottom = iHeight;
  190. CalcAspectScaledRect(prgSize, pRect);
  191. }
  192. else
  193. {
  194. // if the bitmap was smaller than the thumbnail, just center it.
  195. pRect->left = (prgSize->cx - iWidth) / 2;
  196. pRect->top = (prgSize->cy- iHeight) / 2;
  197. pRect->right = pRect->left + iWidth;
  198. pRect->bottom = pRect->top + iHeight;
  199. }
  200. }
  201. STDMETHODIMP CImgCtxThumb::Extract(HBITMAP * phBmpThumbnail)
  202. {
  203. m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  204. if (!m_hEvent)
  205. {
  206. return E_OUTOFMEMORY;
  207. }
  208. m_phBmp = phBmpThumbnail;
  209. return InternalResume();
  210. }
  211. STDMETHODIMP CImgCtxThumb::GetDateStamp(FILETIME * pftTimeStamp)
  212. {
  213. HRESULT hr = E_FAIL;
  214. ASSERT(pftTimeStamp);
  215. WIN32_FIND_DATAW rgData;
  216. WCHAR szBuffer[MAX_PATH];
  217. DWORD dwSize = ARRAYSIZE(szBuffer);
  218. hr = PathCreateFromUrlW(m_szPath, szBuffer, &dwSize, URL_UNESCAPE);
  219. if (SUCCEEDED(hr))
  220. {
  221. HANDLE hFind = FindFirstFileW(szBuffer, &rgData);
  222. if (INVALID_HANDLE_VALUE != hFind)
  223. {
  224. *pftTimeStamp = rgData.ftLastWriteTime;
  225. FindClose(hFind);
  226. }
  227. else
  228. {
  229. hr = E_FAIL;
  230. }
  231. }
  232. return hr;
  233. }
  234. STDMETHODIMP CImgCtxThumb::GetClassID(CLSID *pClassID)
  235. {
  236. return E_NOTIMPL;
  237. }
  238. STDMETHODIMP CImgCtxThumb::IsDirty()
  239. {
  240. return E_NOTIMPL;
  241. }
  242. STDMETHODIMP CImgCtxThumb::Load(LPCOLESTR pszFileName, DWORD dwMode)
  243. {
  244. if (!pszFileName)
  245. {
  246. return E_INVALIDARG;
  247. }
  248. if (lstrlenW(pszFileName) > ARRAYSIZE(m_szPath) - 6)
  249. {
  250. return E_FAIL;
  251. }
  252. DWORD dwSize = ARRAYSIZE(m_szPath);
  253. return UrlCreateFromPathW(pszFileName, m_szPath, &dwSize, URL_ESCAPE_UNSAFE);
  254. }
  255. STDMETHODIMP CImgCtxThumb::Save(LPCOLESTR pszFileName, BOOL fRemember)
  256. {
  257. return E_NOTIMPL;
  258. }
  259. STDMETHODIMP CImgCtxThumb::SaveCompleted(LPCOLESTR pszFileName)
  260. {
  261. return E_NOTIMPL;
  262. }
  263. STDMETHODIMP CImgCtxThumb::GetCurFile(LPOLESTR *ppszFileName)
  264. {
  265. return E_NOTIMPL;
  266. }
  267. STDMETHODIMP CImgCtxThumb::Run()
  268. {
  269. return E_NOTIMPL;
  270. }
  271. STDMETHODIMP CImgCtxThumb::Kill(BOOL fUnused)
  272. {
  273. LONG lRes = InterlockedExchange(& m_lState, IRTIR_TASK_PENDING);
  274. if (lRes != IRTIR_TASK_RUNNING)
  275. {
  276. m_lState = lRes;
  277. }
  278. if (m_hEvent)
  279. SetEvent(m_hEvent);
  280. return S_OK;
  281. }
  282. STDMETHODIMP CImgCtxThumb::Resume()
  283. {
  284. if (m_lState != IRTIR_TASK_SUSPENDED)
  285. {
  286. return S_FALSE;
  287. }
  288. return InternalResume();
  289. }
  290. STDMETHODIMP CImgCtxThumb::Suspend()
  291. {
  292. LONG lRes = InterlockedExchange(& m_lState, IRTIR_TASK_SUSPENDED);
  293. if (lRes != IRTIR_TASK_RUNNING)
  294. {
  295. m_lState = lRes;
  296. }
  297. if (m_hEvent)
  298. SetEvent(m_hEvent);
  299. return S_OK;
  300. }
  301. STDMETHODIMP_(ULONG) CImgCtxThumb::IsRunning()
  302. {
  303. return m_lState;
  304. }
  305. STDMETHODIMP CImgCtxThumb::InternalResume()
  306. {
  307. if (m_phBmp == NULL)
  308. {
  309. return E_UNEXPECTED;
  310. }
  311. m_lState = IRTIR_TASK_RUNNING;
  312. HRESULT hr = S_OK;
  313. if (!m_pImg)
  314. {
  315. hr = CoCreateInstance(CLSID_IImgCtx, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IImgCtx, &m_pImg));
  316. if (SUCCEEDED(hr))
  317. {
  318. ASSERT(m_pImg);
  319. hr = m_pImg->Load(m_szPath, DWN_RAWIMAGE | m_dwRecClrDepth);
  320. if (SUCCEEDED(hr))
  321. {
  322. hr = m_pImg->SetCallback(OnImgCtxChange, this);
  323. }
  324. if (SUCCEEDED(hr))
  325. {
  326. hr = m_pImg->SelectChanges(IMGCHG_COMPLETE, 0, TRUE);
  327. }
  328. if (FAILED(hr))
  329. {
  330. ATOMICRELEASE(m_pImg);
  331. m_lState = IRTIR_TASK_FINISHED;
  332. return hr;
  333. }
  334. }
  335. else
  336. {
  337. m_lState = IRTIR_TASK_FINISHED;
  338. return hr;
  339. }
  340. }
  341. ULONG fState;
  342. SIZE rgSize;
  343. m_pImg->GetStateInfo(&fState, &rgSize, TRUE);
  344. if (!(fState & IMGLOAD_COMPLETE))
  345. {
  346. do
  347. {
  348. DWORD dwRet = MsgWaitForMultipleObjects(1, &m_hEvent, FALSE, INFINITE, QS_ALLINPUT);
  349. if (dwRet != WAIT_OBJECT_0)
  350. {
  351. // check the event anyway, msgs get checked first, so
  352. // it could take a while for this to get fired otherwise..
  353. dwRet = WaitForSingleObject(m_hEvent, 0);
  354. }
  355. if (dwRet == WAIT_OBJECT_0)
  356. {
  357. break;
  358. }
  359. MSG msg;
  360. // empty the message queue...
  361. while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  362. {
  363. if ((msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) ||
  364. (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST && msg.message != WM_MOUSEMOVE))
  365. {
  366. continue;
  367. }
  368. TranslateMessage(&msg);
  369. DispatchMessage(&msg);
  370. }
  371. } while (TRUE);
  372. // check why we broke out...
  373. if (m_lState == IRTIR_TASK_PENDING)
  374. {
  375. m_lState = IRTIR_TASK_FINISHED;
  376. m_pImg->Disconnect();
  377. ATOMICRELEASE(m_pImg);
  378. return E_FAIL;
  379. }
  380. if (m_lState == IRTIR_TASK_SUSPENDED)
  381. return E_PENDING;
  382. m_pImg->GetStateInfo(&fState, &rgSize, TRUE);
  383. }
  384. hr = (fState & IMGLOAD_ERROR) ? E_FAIL : S_OK;
  385. if (SUCCEEDED(hr))
  386. {
  387. HDC hdc = GetDC(NULL);
  388. // LINTASSERT(hdc || !hdc); // 0 semi-ok
  389. void *lpBits;
  390. HDC hdcBmp = CreateCompatibleDC(hdc);
  391. if (hdcBmp && hdc)
  392. {
  393. struct {
  394. BITMAPINFOHEADER bi;
  395. DWORD ct[256];
  396. } dib;
  397. dib.bi.biSize = sizeof(BITMAPINFOHEADER);
  398. // On NT5 we go directly to the thumbnail with StretchBlt
  399. // on other OS's we make a full size copy and pass the bits
  400. // to ScaleSharpen2().
  401. if (IsOS(OS_WIN2000ORGREATER))
  402. {
  403. dib.bi.biWidth = m_rgSize.cx;
  404. dib.bi.biHeight = m_rgSize.cy;
  405. }
  406. else
  407. {
  408. dib.bi.biWidth = rgSize.cx;
  409. dib.bi.biHeight = rgSize.cy;
  410. }
  411. dib.bi.biPlanes = 1;
  412. dib.bi.biBitCount = (WORD) m_dwRecClrDepth;
  413. dib.bi.biCompression = BI_RGB;
  414. dib.bi.biSizeImage = 0;
  415. dib.bi.biXPelsPerMeter = 0;
  416. dib.bi.biYPelsPerMeter = 0;
  417. dib.bi.biClrUsed = (m_dwRecClrDepth <= 8) ? (1 << m_dwRecClrDepth) : 0;
  418. dib.bi.biClrImportant = 0;
  419. HPALETTE hpal = NULL;
  420. HPALETTE hpalOld = NULL;
  421. if (m_dwRecClrDepth <= 8)
  422. {
  423. if (m_dwRecClrDepth == 8)
  424. {
  425. // need to get the right palette....
  426. hr = m_pImg->GetPalette(& hpal);
  427. }
  428. else
  429. {
  430. hpal = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
  431. }
  432. if (SUCCEEDED(hr) && hpal)
  433. {
  434. hpalOld = SelectPalette(hdcBmp, hpal, TRUE);
  435. // LINTASSERT(hpalOld || !hpalOld); // 0 semi-ok for SelectPalette
  436. RealizePalette(hdcBmp);
  437. int n = GetPaletteEntries(hpal, 0, 256, (LPPALETTEENTRY)&dib.ct[0]);
  438. ASSERT(n >= (int) dib.bi.biClrUsed);
  439. for (int i = 0; i < (int)dib.bi.biClrUsed; i ++)
  440. dib.ct[i] = RGB(GetBValue(dib.ct[i]),GetGValue(dib.ct[i]),GetRValue(dib.ct[i]));
  441. }
  442. }
  443. HBITMAP hBmp = CreateDIBSection(hdcBmp, (LPBITMAPINFO)&dib, DIB_RGB_COLORS, &lpBits, NULL, 0);
  444. if (hBmp != NULL)
  445. {
  446. HGDIOBJ hOld = SelectObject(hdcBmp, hBmp);
  447. // On NT5 Go directly to the Thumbnail with StretchBlt()
  448. if (IsOS(OS_WIN2000ORGREATER))
  449. {
  450. // Compute output size of thumbnail
  451. RECT rectThumbnail;
  452. rectThumbnail.left = 0;
  453. rectThumbnail.top = 0;
  454. rectThumbnail.right = m_rgSize.cx;
  455. rectThumbnail.bottom = m_rgSize.cy;
  456. FillRect(hdcBmp, &rectThumbnail, (HBRUSH) (COLOR_WINDOW+1));
  457. rectThumbnail.right = rgSize.cx;
  458. rectThumbnail.bottom = rgSize.cy;
  459. CalculateAspectRatio (&m_rgSize, &rectThumbnail);
  460. // Call DanielC for the StretchBlt
  461. SetStretchBltMode (hdcBmp, HALFTONE);
  462. // Create the thumbnail
  463. m_pImg->StretchBlt(hdcBmp,
  464. rectThumbnail.left,
  465. rectThumbnail.top,
  466. rectThumbnail.right - rectThumbnail.left,
  467. rectThumbnail.bottom - rectThumbnail.top,
  468. 0, 0,
  469. rgSize.cx,
  470. rgSize.cy,
  471. SRCCOPY);
  472. SelectObject(hdcBmp, hOld);
  473. *m_phBmp = hBmp;
  474. }
  475. else
  476. {
  477. //
  478. // On systems other than NT5 make a full size copy of
  479. // the bits and pass the copy to ScaleSharpen2().
  480. //
  481. RECT rectThumbnail;
  482. rectThumbnail.left = 0;
  483. rectThumbnail.top = 0;
  484. rectThumbnail.right = rgSize.cx;
  485. rectThumbnail.bottom = rgSize.cy;
  486. FillRect(hdcBmp, &rectThumbnail, (HBRUSH) (COLOR_WINDOW+1));
  487. m_pImg->StretchBlt(hdcBmp,
  488. 0, 0,
  489. rgSize.cx,
  490. rgSize.cy,
  491. 0, 0,
  492. rgSize.cx,
  493. rgSize.cy,
  494. SRCCOPY);
  495. SelectObject(hdcBmp, hOld);
  496. if (m_rgSize.cx == rgSize.cx && m_rgSize.cy == rgSize.cy)
  497. {
  498. *m_phBmp = hBmp;
  499. }
  500. else
  501. {
  502. SIZEL rgCur;
  503. rgCur.cx = rgSize.cx;
  504. rgCur.cy = rgSize.cy;
  505. IScaleAndSharpenImage2 * pScale;
  506. hr = CoCreateInstance(CLSID_ThumbnailScaler, NULL, CLSCTX_INPROC_SERVER,
  507. IID_PPV_ARG(IScaleAndSharpenImage2, &pScale));
  508. if (SUCCEEDED(hr))
  509. {
  510. hr = pScale->ScaleSharpen2((BITMAPINFO *) &dib,
  511. lpBits,
  512. m_phBmp,
  513. &m_rgSize,
  514. m_dwRecClrDepth,
  515. hpal,
  516. 20, m_fOrigSize);
  517. pScale->Release();
  518. }
  519. DeleteObject(hBmp);
  520. }
  521. }
  522. }
  523. if (SUCCEEDED(hr) && hpal && m_dwRecClrDepth <= 8)
  524. {
  525. (void) SelectPalette(hdcBmp, hpalOld, TRUE);
  526. RealizePalette(hdcBmp);
  527. }
  528. if (m_dwRecClrDepth < 8)
  529. {
  530. // we used a stock 16 colour palette
  531. DeletePalette(hpal);
  532. }
  533. }
  534. if (hdc)
  535. {
  536. ReleaseDC(NULL, hdc);
  537. }
  538. if (hdcBmp)
  539. {
  540. DeleteDC(hdcBmp);
  541. }
  542. }
  543. m_pImg->Disconnect();
  544. ATOMICRELEASE(m_pImg);
  545. m_lState = IRTIR_TASK_FINISHED;
  546. return hr;
  547. }