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.

1026 lines
30 KiB

  1. #include "shellprv.h"
  2. #include "ocmm.h"
  3. #include "thumbutil.h"
  4. typedef UCHAR BGR3[3];
  5. class CThumbnailMaker
  6. {
  7. public:
  8. CThumbnailMaker();
  9. ~CThumbnailMaker();
  10. void Scale(BGR3 *pDst, UINT uiDstWidth, int iDstStep, const BGR3 *pSrc, UINT uiSrcWidth, int iSrcStep);
  11. HRESULT Init(UINT uiDstWidth, UINT uiDstHeight, UINT uiSrcWidth, UINT uiSrcHeight);
  12. HRESULT AddScanline(UCHAR *pucSrc, UINT uiY);
  13. HRESULT AddDIB(BITMAPINFO *pBMI);
  14. HRESULT AddDIBSECTION(BITMAPINFO *pBMI, void *pBits);
  15. HRESULT GetBITMAPINFO(BITMAPINFO **ppBMInfo, DWORD *pdwSize);
  16. HRESULT GetSharpenedBITMAPINFO(UINT uiSharpPct, BITMAPINFO **ppBMInfo, DWORD *pdwSize);
  17. private:
  18. UINT _uiDstWidth, _uiDstHeight;
  19. UINT _uiSrcWidth, _uiSrcHeight;
  20. BGR3 *_pImH;
  21. };
  22. CThumbnailMaker::CThumbnailMaker()
  23. {
  24. _pImH = NULL;
  25. }
  26. CThumbnailMaker::~CThumbnailMaker()
  27. {
  28. if (_pImH)
  29. delete[] _pImH;
  30. }
  31. HRESULT CThumbnailMaker::Init(UINT uiDstWidth, UINT uiDstHeight, UINT uiSrcWidth, UINT uiSrcHeight)
  32. {
  33. _uiDstWidth = uiDstWidth;
  34. _uiDstHeight = uiDstHeight;
  35. _uiSrcWidth = uiSrcWidth;
  36. _uiSrcHeight = uiSrcHeight;
  37. if (_uiDstWidth < 1 || _uiDstHeight < 1 ||
  38. _uiSrcWidth < 1 || _uiSrcHeight < 1)
  39. return E_INVALIDARG;
  40. if (_pImH)
  41. delete[] _pImH;
  42. _pImH = new BGR3[_uiDstWidth * _uiSrcHeight];
  43. if (_pImH == NULL)
  44. return E_OUTOFMEMORY;
  45. return S_OK;
  46. }
  47. void CThumbnailMaker::Scale( BGR3 *pDst, UINT dxDst, int iDstBytStep,
  48. const BGR3 *pSrc, UINT dxSrc, int iSrcBytStep)
  49. {
  50. int mnum = dxSrc;
  51. int mden = dxDst;
  52. // Scaling up, use a triangle filter.
  53. if (mden >= mnum)
  54. {
  55. int frac = 0;
  56. // Adjust the slope so that we calculate the fraction of the
  57. // "next" pixel to use (i.e. should be 0 for the first and
  58. // last dst pixel).
  59. --mnum;
  60. if (--mden == 0)
  61. mden = 0; // avoid div by 0
  62. BGR3 *pSrc1 = (BGR3 *)(((UCHAR *)pSrc) + iSrcBytStep);
  63. for (UINT x = 0; x < dxDst; x++)
  64. {
  65. if (frac == 0)
  66. {
  67. (*pDst)[0] = (*pSrc)[0];
  68. (*pDst)[1] = (*pSrc)[1];
  69. (*pDst)[2] = (*pSrc)[2];
  70. }
  71. else
  72. {
  73. (*pDst)[0] = ((mden - frac) * (*pSrc)[0] + frac * (*pSrc1)[0]) / mden;
  74. (*pDst)[1] = ((mden - frac) * (*pSrc)[1] + frac * (*pSrc1)[1]) / mden;
  75. (*pDst)[2] = ((mden - frac) * (*pSrc)[2] + frac * (*pSrc1)[2]) / mden;
  76. }
  77. pDst = (BGR3 *)((UCHAR *)pDst + iDstBytStep);
  78. frac += mnum;
  79. if (frac >= mden)
  80. {
  81. frac -= mden;
  82. pSrc = (BGR3 *)((UCHAR *)pSrc + iSrcBytStep);
  83. pSrc1 = (BGR3 *)((UCHAR *)pSrc1 + iSrcBytStep);
  84. }
  85. }
  86. }
  87. // Scaling down, use a box filter.
  88. else
  89. {
  90. int frac = 0;
  91. for (UINT x = 0; x < dxDst; x++)
  92. {
  93. UINT uiSum[3] = {0, 0, 0};
  94. UINT uiCnt = 0;
  95. frac += mnum;
  96. while (frac >= mden)
  97. {
  98. uiSum[0] += (*pSrc)[0];
  99. uiSum[1] += (*pSrc)[1];
  100. uiSum[2] += (*pSrc)[2];
  101. uiCnt++;
  102. frac -= mden;
  103. pSrc = (BGR3 *)((UCHAR *)pSrc + iSrcBytStep);
  104. }
  105. (*pDst)[0] = uiSum[0] / uiCnt;
  106. (*pDst)[1] = uiSum[1] / uiCnt;
  107. (*pDst)[2] = uiSum[2] / uiCnt;
  108. pDst = (BGR3 *)((UCHAR *)pDst + iDstBytStep);
  109. }
  110. }
  111. }
  112. //
  113. // For AddScanline, we scale the input horizontally into our temporary
  114. // image buffer.
  115. //
  116. HRESULT CThumbnailMaker::AddScanline(UCHAR *pSrc, UINT uiY)
  117. {
  118. if (pSrc == NULL || uiY >= _uiSrcHeight)
  119. return E_INVALIDARG;
  120. Scale(_pImH + uiY * _uiDstWidth, _uiDstWidth, sizeof(BGR3), (BGR3 *)pSrc, _uiSrcWidth, sizeof(BGR3));
  121. return S_OK;
  122. }
  123. // For GetBITMAPINFO, we complete the scaling vertically and return the
  124. // result as a DIB.
  125. HRESULT CThumbnailMaker::GetBITMAPINFO(BITMAPINFO **ppBMInfo, DWORD *pdwSize)
  126. {
  127. *ppBMInfo = NULL;
  128. DWORD dwBPL = (((_uiDstWidth * 24) + 31) >> 3) & ~3;
  129. DWORD dwTotSize = sizeof(BITMAPINFOHEADER) + dwBPL * _uiDstHeight;
  130. BITMAPINFO *pBMI = (BITMAPINFO *)CoTaskMemAlloc(dwTotSize);
  131. if (pBMI == NULL)
  132. return E_OUTOFMEMORY;
  133. BITMAPINFOHEADER *pBMIH = &pBMI->bmiHeader;
  134. pBMIH->biSize = sizeof(*pBMIH);
  135. pBMIH->biWidth = _uiDstWidth;
  136. pBMIH->biHeight = _uiDstHeight;
  137. pBMIH->biPlanes = 1;
  138. pBMIH->biBitCount = 24;
  139. pBMIH->biCompression = BI_RGB;
  140. pBMIH->biXPelsPerMeter = 0;
  141. pBMIH->biYPelsPerMeter = 0;
  142. pBMIH->biSizeImage = dwBPL * _uiDstHeight;
  143. pBMIH->biClrUsed = 0;
  144. pBMIH->biClrImportant = 0;
  145. UCHAR *pDst = (UCHAR *)pBMIH + pBMIH->biSize + (_uiDstHeight - 1) * dwBPL;
  146. for (UINT x = 0; x < _uiDstWidth; x++)
  147. {
  148. Scale((BGR3 *)pDst + x, _uiDstHeight, -(int)dwBPL,
  149. _pImH + x, _uiSrcHeight, _uiDstWidth * sizeof(BGR3));
  150. }
  151. *ppBMInfo = pBMI;
  152. *pdwSize = dwTotSize;
  153. return S_OK;
  154. }
  155. HRESULT CThumbnailMaker::GetSharpenedBITMAPINFO(UINT uiSharpPct, BITMAPINFO **ppBMInfo, DWORD *pdwSize)
  156. {
  157. #define SCALE 10000
  158. if (uiSharpPct > 100)
  159. return E_INVALIDARG;
  160. // Get the unsharpened bitmap.
  161. DWORD dwSize;
  162. HRESULT hr = GetBITMAPINFO(ppBMInfo, &dwSize);
  163. if (FAILED(hr))
  164. return hr;
  165. *pdwSize = dwSize;
  166. // Create a duplicate to serve as the original.
  167. BITMAPINFO *pBMISrc = (BITMAPINFO *)new UCHAR[dwSize];
  168. if (pBMISrc == NULL)
  169. {
  170. delete *ppBMInfo;
  171. return E_OUTOFMEMORY;
  172. }
  173. memcpy(pBMISrc, *ppBMInfo, dwSize);
  174. int bpl = (pBMISrc->bmiHeader.biWidth * 3 + 3) & ~3;
  175. //
  176. // Sharpen inside a 1 pixel border
  177. //
  178. UCHAR *pucDst = (UCHAR *)*ppBMInfo + sizeof(BITMAPINFOHEADER);
  179. UCHAR *pucSrc[3];
  180. pucSrc[0] = (UCHAR *)pBMISrc + sizeof(BITMAPINFOHEADER);
  181. pucSrc[1] = pucSrc[0] + bpl;
  182. pucSrc[2] = pucSrc[1] + bpl;
  183. int wdiag = (10355 * uiSharpPct) / 100;
  184. int wadj = (14645 * uiSharpPct) / 100;
  185. int wcent = 4 * (wdiag + wadj);
  186. for (int y = 1; y < pBMISrc->bmiHeader.biHeight-1; ++y)
  187. {
  188. for (int x = 3*(pBMISrc->bmiHeader.biWidth-2); x >= 3; --x)
  189. {
  190. int v = pucDst[x] +
  191. (pucSrc[1][x] * wcent -
  192. ((pucSrc[0][x - 3] +
  193. pucSrc[0][x + 3] +
  194. pucSrc[2][x - 3] +
  195. pucSrc[2][x + 3]) * wdiag +
  196. (pucSrc[0][x] +
  197. pucSrc[1][x - 3] +
  198. pucSrc[1][x + 3] +
  199. pucSrc[2][x]) * wadj)) / SCALE;
  200. pucDst[x] = v < 0 ? 0 : v > 255 ? 255 : v;
  201. }
  202. pucDst += bpl;
  203. pucSrc[0] = pucSrc[1];
  204. pucSrc[1] = pucSrc[2];
  205. pucSrc[2] += bpl;
  206. }
  207. delete[] pBMISrc;
  208. return S_OK;
  209. #undef SCALE
  210. }
  211. HRESULT ThumbnailMaker_Create(CThumbnailMaker **ppThumbMaker)
  212. {
  213. *ppThumbMaker = new CThumbnailMaker;
  214. return *ppThumbMaker ? S_OK : E_OUTOFMEMORY;
  215. }
  216. HRESULT CThumbnailMaker::AddDIB(BITMAPINFO *pBMI)
  217. {
  218. int ncolors = pBMI->bmiHeader.biClrUsed;
  219. if (ncolors == 0 && pBMI->bmiHeader.biBitCount <= 8)
  220. ncolors = 1 << pBMI->bmiHeader.biBitCount;
  221. if (pBMI->bmiHeader.biBitCount == 16 ||
  222. pBMI->bmiHeader.biBitCount == 32)
  223. {
  224. if (pBMI->bmiHeader.biCompression == BI_BITFIELDS)
  225. {
  226. ncolors = 3;
  227. }
  228. }
  229. UCHAR *pBits = (UCHAR *)&pBMI->bmiColors[0] + ncolors * sizeof(RGBQUAD);
  230. return AddDIBSECTION(pBMI, (void *) pBits);
  231. }
  232. HRESULT CThumbnailMaker::AddDIBSECTION(BITMAPINFO *pBMI, void *pBits)
  233. {
  234. RGBQUAD *pRGBQ, *pQ;
  235. UCHAR *pucBits0, *pucBits, *pB, *pucBits240, *pucBits24, *pB24;
  236. int bpl;
  237. int x, y, ncolors;
  238. ULONG rmask, gmask, bmask;
  239. int rshift, gshift, bshift;
  240. HRESULT hr;
  241. //
  242. // Make sure that thumbnail maker has been properly initialized.
  243. //
  244. if (pBMI == NULL)
  245. return E_INVALIDARG;
  246. if (pBMI->bmiHeader.biWidth != (LONG)_uiSrcWidth ||
  247. pBMI->bmiHeader.biHeight != (LONG)_uiSrcHeight)
  248. return E_INVALIDARG;
  249. //
  250. // Don't handle RLE.
  251. //
  252. if (pBMI->bmiHeader.biCompression != BI_RGB &&
  253. pBMI->bmiHeader.biCompression != BI_BITFIELDS)
  254. return E_INVALIDARG;
  255. pRGBQ = (RGBQUAD *)&pBMI->bmiColors[0];
  256. ncolors = pBMI->bmiHeader.biClrUsed;
  257. if (ncolors == 0 && pBMI->bmiHeader.biBitCount <= 8)
  258. ncolors = 1 << pBMI->bmiHeader.biBitCount;
  259. //
  260. // Decode 16/32bpp with masks.
  261. //
  262. if (pBMI->bmiHeader.biBitCount == 16 ||
  263. pBMI->bmiHeader.biBitCount == 32)
  264. {
  265. if (pBMI->bmiHeader.biCompression == BI_BITFIELDS)
  266. {
  267. rmask = ((ULONG *)pRGBQ)[0];
  268. gmask = ((ULONG *)pRGBQ)[1];
  269. bmask = ((ULONG *)pRGBQ)[2];
  270. ncolors = 3;
  271. }
  272. else if (pBMI->bmiHeader.biBitCount == 16)
  273. {
  274. rmask = 0x7c00;
  275. gmask = 0x03e0;
  276. bmask = 0x001f;
  277. }
  278. else /* 32 */
  279. {
  280. rmask = 0xff0000;
  281. gmask = 0x00ff00;
  282. bmask = 0x0000ff;
  283. }
  284. for (rshift = 0; (rmask & 1) == 0; rmask >>= 1, ++rshift);
  285. if (rmask == 0)
  286. rmask = 1;
  287. for (gshift = 0; (gmask & 1) == 0; gmask >>= 1, ++gshift);
  288. if (gmask == 0)
  289. gmask = 1;
  290. for (bshift = 0; (bmask & 1) == 0; bmask >>= 1, ++bshift);
  291. if (bmask == 0)
  292. bmask = 1;
  293. }
  294. bpl = ((pBMI->bmiHeader.biBitCount * _uiSrcWidth + 31) >> 3) & ~3;
  295. pucBits0 = (UCHAR *) pBits;
  296. pucBits = pucBits0;
  297. if (pBMI->bmiHeader.biBitCount == 24)
  298. pucBits240 = pucBits;
  299. else
  300. {
  301. int bpl24 = (_uiSrcWidth * 3 + 3) & ~3;
  302. pucBits240 = new UCHAR[bpl24];
  303. if (pucBits240 == NULL)
  304. return E_OUTOFMEMORY;
  305. }
  306. pucBits24 = pucBits240;
  307. hr = S_OK;
  308. for (y = 0; y < (int)_uiSrcHeight; ++y)
  309. {
  310. pB = pucBits;
  311. pB24 = pucBits24;
  312. switch (pBMI->bmiHeader.biBitCount)
  313. {
  314. case 1:
  315. for (x = _uiSrcWidth; x >= 8; x -= 8)
  316. {
  317. pQ = &pRGBQ[(*pB >> 7) & 1];
  318. *pB24++ = pQ->rgbBlue;
  319. *pB24++ = pQ->rgbGreen;
  320. *pB24++ = pQ->rgbRed;
  321. pQ = &pRGBQ[(*pB >> 6) & 1];
  322. *pB24++ = pQ->rgbBlue;
  323. *pB24++ = pQ->rgbGreen;
  324. *pB24++ = pQ->rgbRed;
  325. pQ = &pRGBQ[(*pB >> 5) & 1];
  326. *pB24++ = pQ->rgbBlue;
  327. *pB24++ = pQ->rgbGreen;
  328. *pB24++ = pQ->rgbRed;
  329. pQ = &pRGBQ[(*pB >> 4) & 1];
  330. *pB24++ = pQ->rgbBlue;
  331. *pB24++ = pQ->rgbGreen;
  332. *pB24++ = pQ->rgbRed;
  333. pQ = &pRGBQ[(*pB >> 3) & 1];
  334. *pB24++ = pQ->rgbBlue;
  335. *pB24++ = pQ->rgbGreen;
  336. *pB24++ = pQ->rgbRed;
  337. pQ = &pRGBQ[(*pB >> 2) & 1];
  338. *pB24++ = pQ->rgbBlue;
  339. *pB24++ = pQ->rgbGreen;
  340. *pB24++ = pQ->rgbRed;
  341. pQ = &pRGBQ[(*pB >> 1) & 1];
  342. *pB24++ = pQ->rgbBlue;
  343. *pB24++ = pQ->rgbGreen;
  344. *pB24++ = pQ->rgbRed;
  345. pQ = &pRGBQ[(*pB++) & 1];
  346. *pB24++ = pQ->rgbBlue;
  347. *pB24++ = pQ->rgbGreen;
  348. *pB24++ = pQ->rgbRed;
  349. }
  350. if (x > 0)
  351. {
  352. int shf = 8;
  353. do
  354. {
  355. pQ = &pRGBQ[(*pB >> --shf) & 1];
  356. *pB24++ = pQ->rgbBlue;
  357. *pB24++ = pQ->rgbGreen;
  358. *pB24++ = pQ->rgbRed;
  359. }
  360. while (--x);
  361. }
  362. break;
  363. case 4:
  364. for (x = _uiSrcWidth; x >= 2; x -= 2)
  365. {
  366. pQ = &pRGBQ[(*pB >> 4) & 0xf];
  367. *pB24++ = pQ->rgbBlue;
  368. *pB24++ = pQ->rgbGreen;
  369. *pB24++ = pQ->rgbRed;
  370. pQ = &pRGBQ[*pB++ & 0xf];
  371. *pB24++ = pQ->rgbBlue;
  372. *pB24++ = pQ->rgbGreen;
  373. *pB24++ = pQ->rgbRed;
  374. }
  375. if (x > 0)
  376. {
  377. pQ = &pRGBQ[(*pB >> 4) & 0xf];
  378. *pB24++ = pQ->rgbBlue;
  379. *pB24++ = pQ->rgbGreen;
  380. *pB24++ = pQ->rgbRed;
  381. if (x > 1)
  382. {
  383. pQ = &pRGBQ[*pB & 0xf];
  384. *pB24++ = pQ->rgbBlue;
  385. *pB24++ = pQ->rgbGreen;
  386. *pB24++ = pQ->rgbRed;
  387. }
  388. }
  389. break;
  390. case 8:
  391. for (x = _uiSrcWidth; x--;)
  392. {
  393. pQ = &pRGBQ[*pB++];
  394. *pB24++ = pQ->rgbBlue;
  395. *pB24++ = pQ->rgbGreen;
  396. *pB24++ = pQ->rgbRed;
  397. }
  398. break;
  399. case 16:
  400. {
  401. USHORT *pW = (USHORT *)pucBits;
  402. for (x = _uiSrcWidth; x--;)
  403. {
  404. ULONG w = *pW++;
  405. *pB24++ = (UCHAR)
  406. ((((w >> bshift) & bmask) * 255) / bmask);
  407. *pB24++ = (UCHAR)
  408. ((((w >> gshift) & gmask) * 255) / gmask);
  409. *pB24++ = (UCHAR)
  410. ((((w >> rshift) & rmask) * 255) / rmask);
  411. }
  412. break;
  413. }
  414. case 24:
  415. pucBits24 = pucBits;
  416. break;
  417. case 32:
  418. {
  419. ULONG *pD;
  420. pD = (ULONG *)pucBits;
  421. for (x = _uiSrcWidth; x--;)
  422. {
  423. ULONG d = *pD++;
  424. *pB24++ = (UCHAR)
  425. ((((d >> bshift) & bmask) * 255) / bmask);
  426. *pB24++ = (UCHAR)
  427. ((((d >> gshift) & gmask) * 255) / gmask);
  428. *pB24++ = (UCHAR)
  429. ((((d >> rshift) & rmask) * 255) / rmask);
  430. }
  431. break;
  432. }
  433. default:
  434. delete[] pucBits24;
  435. return E_INVALIDARG;
  436. }
  437. hr = AddScanline(pucBits24, (_uiSrcHeight-1) - y);
  438. if (FAILED(hr))
  439. break;
  440. pucBits += bpl;
  441. }
  442. if (pucBits240 != pucBits0)
  443. delete[] pucBits240;
  444. return hr;
  445. }
  446. UINT CalcImageSize(const SIZE *prgSize, DWORD dwClrDepth)
  447. {
  448. UINT uSize = prgSize->cx * dwClrDepth;
  449. uSize *= (prgSize->cy < 0) ? (- prgSize->cy) : prgSize->cy;
  450. // divide by 8
  451. UINT uRetVal = uSize >> 3;
  452. if (uSize & 7)
  453. {
  454. uRetVal++;
  455. }
  456. return uRetVal;
  457. }
  458. BOOL ConvertDIBSECTIONToThumbnail(BITMAPINFO *pbi, void *pBits,
  459. HBITMAP *phBmpThumbnail, const SIZE *prgSize,
  460. DWORD dwRecClrDepth, HPALETTE hpal, UINT uiSharpPct, BOOL fOrigSize)
  461. {
  462. BITMAPINFO *pbiScaled = pbi, *pbiUsed = pbi;
  463. BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)pbi;
  464. BOOL bRetVal = FALSE, bInverted = FALSE;
  465. RECT rect;
  466. HRESULT hr;
  467. void *pScaledBits = pBits;
  468. // the scaling code doesn't handle inverted bitmaps, so we treat
  469. // them as if they were normal, by inverting the height here and
  470. // then setting it back before doing a paint.
  471. if (pbi->bmiHeader.biHeight < 0)
  472. {
  473. pbi->bmiHeader.biHeight *= -1;
  474. bInverted = TRUE;
  475. }
  476. rect.left = 0;
  477. rect.top = 0;
  478. rect.right = pbih->biWidth;
  479. rect.bottom = pbih->biHeight;
  480. CalculateAspectRatio(prgSize, &rect);
  481. // only bother with the scaling and sharpening if we are messing with the size...
  482. if ((rect.right - rect.left != pbih->biWidth) || (rect.bottom - rect.top != pbih->biHeight))
  483. {
  484. CThumbnailMaker *pThumbMaker;
  485. hr = ThumbnailMaker_Create(&pThumbMaker);
  486. if (SUCCEEDED(hr))
  487. {
  488. // initialize thumbnail maker.
  489. hr = pThumbMaker->Init(rect.right - rect.left, rect.bottom - rect.top,
  490. pbi->bmiHeader.biWidth, abs(pbi->bmiHeader.biHeight));
  491. if (SUCCEEDED(hr))
  492. {
  493. // scale image.
  494. hr = pThumbMaker->AddDIBSECTION(pbiUsed, pBits);
  495. if (SUCCEEDED(hr))
  496. {
  497. DWORD dwSize;
  498. hr = pThumbMaker->GetSharpenedBITMAPINFO(uiSharpPct, &pbiScaled, &dwSize);
  499. if (SUCCEEDED(hr))
  500. {
  501. pScaledBits = (LPBYTE)pbiScaled + sizeof(BITMAPINFOHEADER);
  502. }
  503. }
  504. }
  505. delete pThumbMaker;
  506. }
  507. if (FAILED(hr))
  508. {
  509. return FALSE;
  510. }
  511. }
  512. // set the height back to negative if that's the way it was before.
  513. if (bInverted == TRUE)
  514. pbiScaled->bmiHeader.biHeight *= -1;
  515. // now if they have asked for origsize rather than the boxed one, and the colour depth is OK, then
  516. // return it...
  517. if (fOrigSize && pbiScaled->bmiHeader.biBitCount <= dwRecClrDepth)
  518. {
  519. SIZE rgCreateSize = { pbiScaled->bmiHeader.biWidth, pbiScaled->bmiHeader.biHeight };
  520. void *pNewBits;
  521. // turn the PbiScaled DIB into a HBITMAP...., note we pass the old biInfo so that it can get the palette form
  522. // it if need be.
  523. bRetVal = CreateSizedDIBSECTION(&rgCreateSize, pbiScaled->bmiHeader.biBitCount, NULL, pbiScaled, phBmpThumbnail, NULL, &pNewBits);
  524. if (bRetVal)
  525. {
  526. // copy the image data accross...
  527. CopyMemory(pNewBits, pScaledBits, CalcImageSize(&rgCreateSize, pbiScaled->bmiHeader.biBitCount));
  528. }
  529. return bRetVal;
  530. }
  531. bRetVal = FactorAspectRatio(pbiScaled, pScaledBits, prgSize, rect,
  532. dwRecClrDepth, hpal, fOrigSize, GetSysColor(COLOR_WINDOW), phBmpThumbnail);
  533. if (pbiScaled != pbi)
  534. {
  535. // free the allocated image...
  536. CoTaskMemFree(pbiScaled);
  537. }
  538. return bRetVal;
  539. }
  540. // This function makes no assumption about whether the thumbnail is square, so
  541. // it calculates the scaling ratio for both dimensions and the uses that as
  542. // the scaling to maintain the aspect ratio.
  543. //
  544. void CalcAspectScaledRect(const SIZE *prgSize, RECT *pRect)
  545. {
  546. ASSERT(pRect->left == 0);
  547. ASSERT(pRect->top == 0);
  548. int iWidth = pRect->right;
  549. int iHeight = pRect->bottom;
  550. int iXRatio = MulDiv(iWidth, 1000, prgSize->cx);
  551. int iYRatio = MulDiv(iHeight, 1000, prgSize->cy);
  552. if (iXRatio > iYRatio)
  553. {
  554. pRect->right = prgSize->cx;
  555. // work out the blank space and split it evenly between the top and the bottom...
  556. int iNewHeight = MulDiv(iHeight, 1000, iXRatio);
  557. if (iNewHeight == 0)
  558. {
  559. iNewHeight = 1;
  560. }
  561. int iRemainder = prgSize->cy - iNewHeight;
  562. pRect->top = iRemainder / 2;
  563. pRect->bottom = iNewHeight + pRect->top;
  564. }
  565. else
  566. {
  567. pRect->bottom = prgSize->cy;
  568. // work out the blank space and split it evenly between the left and the right...
  569. int iNewWidth = MulDiv(iWidth, 1000, iYRatio);
  570. if (iNewWidth == 0)
  571. {
  572. iNewWidth = 1;
  573. }
  574. int iRemainder = prgSize->cx - iNewWidth;
  575. pRect->left = iRemainder / 2;
  576. pRect->right = iNewWidth + pRect->left;
  577. }
  578. }
  579. void CalculateAspectRatio(const SIZE *prgSize, RECT *pRect)
  580. {
  581. int iHeight = abs(pRect->bottom - pRect->top);
  582. int iWidth = abs(pRect->right - pRect->left);
  583. // check if the initial bitmap is larger than the size of the thumbnail.
  584. if (iWidth > prgSize->cx || iHeight > prgSize->cy)
  585. {
  586. pRect->left = 0;
  587. pRect->top = 0;
  588. pRect->right = iWidth;
  589. pRect->bottom = iHeight;
  590. CalcAspectScaledRect(prgSize, pRect);
  591. }
  592. else
  593. {
  594. // if the bitmap was smaller than the thumbnail, just center it.
  595. pRect->left = (prgSize->cx - iWidth) / 2;
  596. pRect->top = (prgSize->cy- iHeight) / 2;
  597. pRect->right = pRect->left + iWidth;
  598. pRect->bottom = pRect->top + iHeight;
  599. }
  600. }
  601. LPBYTE g_pbCMAP = NULL;
  602. STDAPI_(BOOL) FactorAspectRatio(BITMAPINFO *pbiScaled, void *pScaledBits,
  603. const SIZE *prgSize, RECT rect, DWORD dwClrDepth,
  604. HPALETTE hpal, BOOL fOrigSize, COLORREF clrBk, HBITMAP *phBmpThumbnail)
  605. {
  606. HDC hdc = CreateCompatibleDC(NULL);
  607. BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)pbiScaled;
  608. BOOL bRetVal = FALSE;
  609. int iRetVal = GDI_ERROR;
  610. BITMAPINFO * pDitheredInfo = NULL;
  611. void * pDitheredBits = NULL;
  612. HBITMAP hbmpDithered = NULL;
  613. if (hdc)
  614. {
  615. if (dwClrDepth == 8)
  616. {
  617. RGBQUAD *pSrcColors = NULL;
  618. LONG nSrcPitch = pbiScaled->bmiHeader.biWidth;
  619. // we are going to 8 bits per pixel, we had better dither everything
  620. // to the same palette.
  621. GUID guidType = CLSID_NULL;
  622. switch(pbiScaled->bmiHeader.biBitCount)
  623. {
  624. case 32:
  625. guidType = BFID_RGB_32;
  626. nSrcPitch *= sizeof(DWORD);
  627. break;
  628. case 24:
  629. guidType = BFID_RGB_24;
  630. nSrcPitch *= 3;
  631. break;
  632. case 16:
  633. // default is 555
  634. guidType = BFID_RGB_555;
  635. // 5-6-5 bitfields has the second DWORD (the green component) as 0x7e00
  636. if (pbiScaled->bmiHeader.biCompression == BI_BITFIELDS &&
  637. pbiScaled->bmiColors[1].rgbGreen == 0x7E)
  638. {
  639. guidType = BFID_RGB_565;
  640. }
  641. nSrcPitch *= sizeof(WORD);
  642. break;
  643. case 8:
  644. guidType = BFID_RGB_8;
  645. pSrcColors = pbiScaled->bmiColors;
  646. // nSrcPitch is already in bytes...
  647. break;
  648. };
  649. if (nSrcPitch % 4)
  650. {
  651. // round up to the nearest DWORD...
  652. nSrcPitch = nSrcPitch + 4 - (nSrcPitch %4);
  653. }
  654. // we are going to 8bpp
  655. LONG nDestPitch = pbiScaled->bmiHeader.biWidth;
  656. if (nDestPitch % 4)
  657. {
  658. // round up to the nearest DWORD...
  659. nDestPitch = nDestPitch + 4 - (nDestPitch % 4);
  660. }
  661. if (guidType != CLSID_NULL)
  662. {
  663. if (g_pbCMAP == NULL)
  664. {
  665. // we are always going to the shell halftone palette right now, otherwise
  666. // computing this inverse colour map consumes a lot of time (approx 2 seconds on
  667. // a p200)
  668. if (FAILED(SHGetInverseCMAP((BYTE *)&g_pbCMAP, sizeof(g_pbCMAP))))
  669. {
  670. return FALSE;
  671. }
  672. }
  673. SIZE rgDithered = {pbiScaled->bmiHeader.biWidth, pbiScaled->bmiHeader.biHeight};
  674. if (rgDithered.cy < 0)
  675. {
  676. // invert it
  677. rgDithered.cy = -rgDithered.cy;
  678. }
  679. if (CreateSizedDIBSECTION(&rgDithered, dwClrDepth, hpal, NULL, &hbmpDithered, &pDitheredInfo, &pDitheredBits))
  680. {
  681. ASSERT(pDitheredInfo && pDitheredBits);
  682. // dither....
  683. IIntDitherer *pDither;
  684. HRESULT hr = CoCreateInstance(CLSID_IntDitherer, NULL, CLSCTX_INPROC_SERVER,
  685. IID_PPV_ARG(IIntDitherer, &pDither));
  686. if (SUCCEEDED(hr))
  687. {
  688. hr = pDither->DitherTo8bpp((LPBYTE) pDitheredBits, nDestPitch,
  689. (LPBYTE) pScaledBits, nSrcPitch, guidType,
  690. pDitheredInfo->bmiColors, pSrcColors,
  691. g_pbCMAP, 0, 0, rgDithered.cx, rgDithered.cy,
  692. -1, -1);
  693. pDither->Release();
  694. }
  695. if (SUCCEEDED(hr))
  696. {
  697. // if the height was inverted, then invert it in the destination bitmap
  698. if (rgDithered.cy != pbiScaled->bmiHeader.biHeight)
  699. {
  700. pDitheredInfo->bmiHeader.biHeight = - rgDithered.cy;
  701. }
  702. // switch to the new image .....
  703. pbiScaled = pDitheredInfo;
  704. pScaledBits = pDitheredBits;
  705. }
  706. }
  707. }
  708. }
  709. // create thumbnail bitmap and copy image into it.
  710. if (CreateSizedDIBSECTION(prgSize, dwClrDepth, hpal, NULL, phBmpThumbnail, NULL, NULL))
  711. {
  712. HBITMAP hBmpOld = (HBITMAP) SelectObject(hdc, *phBmpThumbnail);
  713. SetStretchBltMode(hdc, COLORONCOLOR);
  714. HGDIOBJ hBrush = CreateSolidBrush(clrBk);
  715. HGDIOBJ hPen = GetStockObject(WHITE_PEN);
  716. HGDIOBJ hOldBrush = SelectObject(hdc, hBrush);
  717. HGDIOBJ hOldPen = SelectObject(hdc, hPen);
  718. HPALETTE hpalOld;
  719. if (hpal)
  720. {
  721. hpalOld = SelectPalette(hdc, hpal, TRUE);
  722. RealizePalette(hdc);
  723. }
  724. SetMapMode(hdc, MM_TEXT);
  725. Rectangle(hdc, 0, 0, prgSize->cx, prgSize->cy);
  726. int iDstHt = rect.bottom - rect.top;
  727. int iDstTop = rect.top, iSrcTop = 0;
  728. if (pbih->biHeight < 0)
  729. {
  730. iDstHt *= -1;
  731. iDstTop = rect.bottom;
  732. iSrcTop = abs(pbih->biHeight);
  733. }
  734. iRetVal = StretchDIBits(hdc, rect.left, iDstTop, rect.right - rect.left, iDstHt,
  735. 0, iSrcTop, pbih->biWidth, pbih->biHeight,
  736. pScaledBits, pbiScaled, DIB_RGB_COLORS, SRCCOPY);
  737. SelectObject(hdc, hOldBrush);
  738. DeleteObject(hBrush);
  739. SelectObject(hdc, hOldPen);
  740. if (hpal)
  741. {
  742. SelectPalette(hdc, hpalOld, TRUE);
  743. RealizePalette(hdc);
  744. }
  745. SelectObject(hdc, hBmpOld);
  746. }
  747. DeleteDC(hdc);
  748. }
  749. if (hbmpDithered)
  750. {
  751. DeleteObject(hbmpDithered);
  752. }
  753. if (pDitheredInfo)
  754. {
  755. LocalFree(pDitheredInfo);
  756. }
  757. return (iRetVal != GDI_ERROR);
  758. }
  759. STDAPI_(BOOL) CreateSizedDIBSECTION(const SIZE *prgSize, DWORD dwClrDepth, HPALETTE hpal,
  760. const BITMAPINFO *pCurInfo, HBITMAP *phBmp, BITMAPINFO **ppBMI, void **ppBits)
  761. {
  762. *phBmp = NULL;
  763. HDC hdc = GetDC(NULL);
  764. if (hdc)
  765. {
  766. HDC hdcBmp = CreateCompatibleDC(hdc);
  767. if (hdcBmp)
  768. {
  769. struct {
  770. BITMAPINFOHEADER bi;
  771. DWORD ct[256];
  772. } dib;
  773. dib.bi.biSize = sizeof(dib.bi);
  774. dib.bi.biWidth = prgSize->cx;
  775. dib.bi.biHeight = prgSize->cy;
  776. dib.bi.biPlanes = 1;
  777. dib.bi.biBitCount = (WORD) dwClrDepth;
  778. dib.bi.biCompression = BI_RGB;
  779. dib.bi.biSizeImage = CalcImageSize(prgSize, dwClrDepth);
  780. dib.bi.biXPelsPerMeter = 0;
  781. dib.bi.biYPelsPerMeter = 0;
  782. dib.bi.biClrUsed = (dwClrDepth <= 8) ? (1 << dwClrDepth) : 0;
  783. dib.bi.biClrImportant = 0;
  784. HPALETTE hpalOld = NULL;
  785. if (dwClrDepth <= 8)
  786. {
  787. // if they passed us the old structure with colour info, and we are the same bit depth, then copy it...
  788. if (pCurInfo && pCurInfo->bmiHeader.biBitCount == dwClrDepth)
  789. {
  790. // use the passed in colour info to generate the DIBSECTION
  791. int iColours = pCurInfo->bmiHeader.biClrUsed;
  792. if (!iColours)
  793. {
  794. iColours = dib.bi.biClrUsed;
  795. }
  796. // copy the data accross...
  797. CopyMemory(dib.ct, pCurInfo->bmiColors, sizeof(RGBQUAD) * iColours);
  798. }
  799. else
  800. {
  801. // need to get the right palette....
  802. hpalOld = SelectPalette(hdcBmp, hpal, TRUE);
  803. RealizePalette(hdcBmp);
  804. int n = GetPaletteEntries(hpal, 0, 256, (LPPALETTEENTRY)&dib.ct[0]);
  805. ASSERT(n >= (int) dib.bi.biClrUsed);
  806. // now convert the PALETTEENTRY to RGBQUAD
  807. for (int i = 0; i < (int)dib.bi.biClrUsed; i ++)
  808. {
  809. dib.ct[i] = RGB(GetBValue(dib.ct[i]),GetGValue(dib.ct[i]),GetRValue(dib.ct[i]));
  810. }
  811. }
  812. }
  813. void *pbits;
  814. *phBmp = CreateDIBSection(hdcBmp, (LPBITMAPINFO)&dib, DIB_RGB_COLORS, &pbits, NULL, 0);
  815. if (*phBmp)
  816. {
  817. if (ppBMI)
  818. {
  819. *ppBMI = (BITMAPINFO *)LocalAlloc(LPTR, sizeof(dib));
  820. if (*ppBMI)
  821. {
  822. CopyMemory(*ppBMI, &dib, sizeof(dib));
  823. }
  824. }
  825. if (ppBits)
  826. {
  827. *ppBits = pbits;
  828. }
  829. }
  830. DeleteDC(hdcBmp);
  831. }
  832. ReleaseDC(NULL, hdc);
  833. }
  834. return (*phBmp != NULL);
  835. }
  836. STDAPI_(void *) CalcBitsOffsetInDIB(BITMAPINFO *pBMI)
  837. {
  838. int ncolors = pBMI->bmiHeader.biClrUsed;
  839. if (ncolors == 0 && pBMI->bmiHeader.biBitCount <= 8)
  840. ncolors = 1 << pBMI->bmiHeader.biBitCount;
  841. if (pBMI->bmiHeader.biBitCount == 16 ||
  842. pBMI->bmiHeader.biBitCount == 32)
  843. {
  844. if (pBMI->bmiHeader.biCompression == BI_BITFIELDS)
  845. {
  846. ncolors = 3;
  847. }
  848. }
  849. return (void *)((UCHAR *)&pBMI->bmiColors[0] + ncolors * sizeof(RGBQUAD));
  850. }