Source code of Windows XP (NT5)
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.

646 lines
18 KiB

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