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.

1045 lines
32 KiB

  1. #include "precomp.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. // exported as helper for thumbnail implementations (used to come from thumbvw.dll)
  459. //
  460. // this code also currently lives in shell32. that should be converted to
  461. // import these APIs (or expose via COM object)
  462. STDAPI_(BOOL) ConvertDIBSECTIONToThumbnail(BITMAPINFO *pbi, void *pBits,
  463. HBITMAP *phBmpThumbnail, const SIZE *prgSize,
  464. DWORD dwRecClrDepth, HPALETTE hpal,
  465. UINT uiSharpPct, BOOL fOrigSize)
  466. {
  467. BITMAPINFO *pbiScaled = pbi, *pbiUsed = pbi;
  468. BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)pbi;
  469. BOOL bRetVal = FALSE, bInverted = FALSE;
  470. RECT rect;
  471. HRESULT hr;
  472. void *pScaledBits = pBits;
  473. // the scaling code doesn't handle inverted bitmaps, so we treat
  474. // them as if they were normal, by inverting the height here and
  475. // then setting it back before doing a paint.
  476. if (pbi->bmiHeader.biHeight < 0)
  477. {
  478. pbi->bmiHeader.biHeight *= -1;
  479. bInverted = TRUE;
  480. }
  481. rect.left = 0;
  482. rect.top = 0;
  483. rect.right = pbih->biWidth;
  484. rect.bottom = pbih->biHeight;
  485. CalculateAspectRatio(prgSize, &rect);
  486. // only bother with the scaling and sharpening if we are messing with the size...
  487. if ((rect.right - rect.left != pbih->biWidth) || (rect.bottom - rect.top != pbih->biHeight))
  488. {
  489. CThumbnailMaker *pThumbMaker;
  490. hr = ThumbnailMaker_Create(&pThumbMaker);
  491. if (SUCCEEDED(hr))
  492. {
  493. // initialize thumbnail maker.
  494. hr = pThumbMaker->Init(rect.right - rect.left, rect.bottom - rect.top,
  495. pbi->bmiHeader.biWidth, abs(pbi->bmiHeader.biHeight));
  496. if (SUCCEEDED(hr))
  497. {
  498. // scale image.
  499. hr = pThumbMaker->AddDIBSECTION(pbiUsed, pBits);
  500. if (SUCCEEDED(hr))
  501. {
  502. DWORD dwSize;
  503. hr = pThumbMaker->GetSharpenedBITMAPINFO(uiSharpPct, &pbiScaled, &dwSize);
  504. if (SUCCEEDED(hr))
  505. {
  506. pScaledBits = (LPBYTE)pbiScaled + sizeof(BITMAPINFOHEADER);
  507. }
  508. }
  509. }
  510. delete pThumbMaker;
  511. }
  512. if (FAILED(hr))
  513. {
  514. return FALSE;
  515. }
  516. }
  517. // set the height back to negative if that's the way it was before.
  518. if (bInverted == TRUE)
  519. pbiScaled->bmiHeader.biHeight *= -1;
  520. // now if they have asked for origsize rather than the boxed one, and the colour depth is OK, then
  521. // return it...
  522. if (fOrigSize && pbiScaled->bmiHeader.biBitCount <= dwRecClrDepth)
  523. {
  524. SIZE rgCreateSize = { pbiScaled->bmiHeader.biWidth, pbiScaled->bmiHeader.biHeight };
  525. void *pNewBits;
  526. // turn the PbiScaled DIB into a HBITMAP...., note we pass the old biInfo so that it can get the palette form
  527. // it if need be.
  528. bRetVal = CreateSizedDIBSECTION(&rgCreateSize, pbiScaled->bmiHeader.biBitCount, NULL, pbiScaled, phBmpThumbnail, NULL, &pNewBits);
  529. if (bRetVal)
  530. {
  531. // copy the image data accross...
  532. CopyMemory(pNewBits, pScaledBits, CalcImageSize(&rgCreateSize, pbiScaled->bmiHeader.biBitCount));
  533. }
  534. return bRetVal;
  535. }
  536. bRetVal = FactorAspectRatio(pbiScaled, pScaledBits, prgSize, rect,
  537. dwRecClrDepth, hpal, fOrigSize, GetSysColor(COLOR_WINDOW), phBmpThumbnail);
  538. if (pbiScaled != pbi)
  539. {
  540. // free the allocated image...
  541. CoTaskMemFree(pbiScaled);
  542. }
  543. return bRetVal;
  544. }
  545. // This function makes no assumption about whether the thumbnail is square, so
  546. // it calculates the scaling ratio for both dimensions and the uses that as
  547. // the scaling to maintain the aspect ratio.
  548. //
  549. // WINDOWS RAID 135065 (toddb): Use of MulDiv should simplify this code
  550. //
  551. void CalcAspectScaledRect(const SIZE *prgSize, RECT *pRect)
  552. {
  553. ASSERT(pRect->left == 0);
  554. ASSERT(pRect->top == 0);
  555. int iWidth = pRect->right;
  556. int iHeight = pRect->bottom;
  557. int iXRatio = (iWidth * 1000) / prgSize->cx;
  558. int iYRatio = (iHeight * 1000) / prgSize->cy;
  559. if (iXRatio > iYRatio)
  560. {
  561. pRect->right = prgSize->cx;
  562. // work out the blank space and split it evenly between the top and the bottom...
  563. int iNewHeight = ((iHeight * 1000) / iXRatio);
  564. if (iNewHeight == 0)
  565. {
  566. iNewHeight = 1;
  567. }
  568. int iRemainder = prgSize->cy - iNewHeight;
  569. pRect->top = iRemainder / 2;
  570. pRect->bottom = iNewHeight + pRect->top;
  571. }
  572. else
  573. {
  574. pRect->bottom = prgSize->cy;
  575. // work out the blank space and split it evenly between the left and the right...
  576. int iNewWidth = ((iWidth * 1000) / iYRatio);
  577. if (iNewWidth == 0)
  578. {
  579. iNewWidth = 1;
  580. }
  581. int iRemainder = prgSize->cx - iNewWidth;
  582. pRect->left = iRemainder / 2;
  583. pRect->right = iNewWidth + pRect->left;
  584. }
  585. }
  586. void CalculateAspectRatio(const SIZE *prgSize, RECT *pRect)
  587. {
  588. int iHeight = abs(pRect->bottom - pRect->top);
  589. int iWidth = abs(pRect->right - pRect->left);
  590. // check if the initial bitmap is larger than the size of the thumbnail.
  591. if (iWidth > prgSize->cx || iHeight > prgSize->cy)
  592. {
  593. pRect->left = 0;
  594. pRect->top = 0;
  595. pRect->right = iWidth;
  596. pRect->bottom = iHeight;
  597. CalcAspectScaledRect(prgSize, pRect);
  598. }
  599. else
  600. {
  601. // if the bitmap was smaller than the thumbnail, just center it.
  602. pRect->left = (prgSize->cx - iWidth) / 2;
  603. pRect->top = (prgSize->cy- iHeight) / 2;
  604. pRect->right = pRect->left + iWidth;
  605. pRect->bottom = pRect->top + iHeight;
  606. }
  607. }
  608. LPBYTE g_pbCMAP = NULL;
  609. STDAPI_(BOOL) FactorAspectRatio(BITMAPINFO *pbiScaled, void *pScaledBits,
  610. const SIZE *prgSize, RECT rect, DWORD dwClrDepth,
  611. HPALETTE hpal, BOOL fOrigSize, COLORREF clrBk, HBITMAP *phBmpThumbnail)
  612. {
  613. HDC hdc = CreateCompatibleDC(NULL);
  614. BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)pbiScaled;
  615. BOOL bRetVal = FALSE;
  616. int iRetVal = GDI_ERROR;
  617. BITMAPINFO * pDitheredInfo = NULL;
  618. void * pDitheredBits = NULL;
  619. HBITMAP hbmpDithered = NULL;
  620. if (hdc)
  621. {
  622. if (dwClrDepth == 8)
  623. {
  624. RGBQUAD *pSrcColors = NULL;
  625. LONG nSrcPitch = pbiScaled->bmiHeader.biWidth;
  626. // we are going to 8 bits per pixel, we had better dither everything
  627. // to the same palette.
  628. GUID guidType = CLSID_NULL;
  629. switch(pbiScaled->bmiHeader.biBitCount)
  630. {
  631. case 32:
  632. guidType = BFID_RGB_32;
  633. nSrcPitch *= sizeof(DWORD);
  634. break;
  635. case 24:
  636. guidType = BFID_RGB_24;
  637. nSrcPitch *= 3;
  638. break;
  639. case 16:
  640. // default is 555
  641. guidType = BFID_RGB_555;
  642. // 5-6-5 bitfields has the second DWORD (the green component) as 0x7e00
  643. if (pbiScaled->bmiHeader.biCompression == BI_BITFIELDS &&
  644. pbiScaled->bmiColors[1].rgbGreen == 0x7E)
  645. {
  646. guidType = BFID_RGB_565;
  647. }
  648. nSrcPitch *= sizeof(WORD);
  649. break;
  650. case 8:
  651. guidType = BFID_RGB_8;
  652. pSrcColors = pbiScaled->bmiColors;
  653. // nSrcPitch is already in bytes...
  654. break;
  655. };
  656. if (nSrcPitch % 4)
  657. {
  658. // round up to the nearest DWORD...
  659. nSrcPitch = nSrcPitch + 4 - (nSrcPitch %4);
  660. }
  661. // we are going to 8bpp
  662. LONG nDestPitch = pbiScaled->bmiHeader.biWidth;
  663. if (nDestPitch % 4)
  664. {
  665. // round up to the nearest DWORD...
  666. nDestPitch = nDestPitch + 4 - (nDestPitch % 4);
  667. }
  668. if (guidType != CLSID_NULL)
  669. {
  670. if (g_pbCMAP == NULL)
  671. {
  672. // we are always going to the shell halftone palette right now, otherwise
  673. // computing this inverse colour map consumes a lot of time (approx 2 seconds on
  674. // a p200)
  675. if (FAILED(SHGetInverseCMAP((BYTE *)&g_pbCMAP, sizeof(g_pbCMAP))))
  676. {
  677. return FALSE;
  678. }
  679. }
  680. SIZE rgDithered = {pbiScaled->bmiHeader.biWidth, pbiScaled->bmiHeader.biHeight};
  681. if (rgDithered.cy < 0)
  682. {
  683. // invert it
  684. rgDithered.cy = -rgDithered.cy;
  685. }
  686. if (CreateSizedDIBSECTION(&rgDithered, dwClrDepth, hpal, NULL, &hbmpDithered, &pDitheredInfo, &pDitheredBits))
  687. {
  688. ASSERT(pDitheredInfo && pDitheredBits);
  689. // dither....
  690. IIntDitherer *pDither;
  691. HRESULT hr = CoCreateInstance(CLSID_IntDitherer, NULL, CLSCTX_INPROC_SERVER,
  692. IID_PPV_ARG(IIntDitherer, &pDither));
  693. if (SUCCEEDED(hr))
  694. {
  695. hr = pDither->DitherTo8bpp((LPBYTE) pDitheredBits, nDestPitch,
  696. (LPBYTE) pScaledBits, nSrcPitch, guidType,
  697. pDitheredInfo->bmiColors, pSrcColors,
  698. g_pbCMAP, 0, 0, rgDithered.cx, rgDithered.cy,
  699. -1, -1);
  700. pDither->Release();
  701. }
  702. if (SUCCEEDED(hr))
  703. {
  704. // if the height was inverted, then invert it in the destination bitmap
  705. if (rgDithered.cy != pbiScaled->bmiHeader.biHeight)
  706. {
  707. pDitheredInfo->bmiHeader.biHeight = - rgDithered.cy;
  708. }
  709. // switch to the new image .....
  710. pbiScaled = pDitheredInfo;
  711. pScaledBits = pDitheredBits;
  712. }
  713. }
  714. }
  715. }
  716. // create thumbnail bitmap and copy image into it.
  717. if (CreateSizedDIBSECTION(prgSize, dwClrDepth, hpal, NULL, phBmpThumbnail, NULL, NULL))
  718. {
  719. HBITMAP hBmpOld = (HBITMAP) SelectObject(hdc, *phBmpThumbnail);
  720. SetStretchBltMode(hdc, COLORONCOLOR);
  721. HGDIOBJ hBrush = CreateSolidBrush(clrBk);
  722. HGDIOBJ hPen = GetStockObject(WHITE_PEN);
  723. HGDIOBJ hOldBrush = SelectObject(hdc, hBrush);
  724. HGDIOBJ hOldPen = SelectObject(hdc, hPen);
  725. HPALETTE hpalOld;
  726. if (hpal)
  727. {
  728. hpalOld = SelectPalette(hdc, hpal, TRUE);
  729. RealizePalette(hdc);
  730. }
  731. SetMapMode(hdc, MM_TEXT);
  732. Rectangle(hdc, 0, 0, prgSize->cx, prgSize->cy);
  733. int iDstHt = rect.bottom - rect.top;
  734. int iDstTop = rect.top, iSrcTop = 0;
  735. if (pbih->biHeight < 0)
  736. {
  737. iDstHt *= -1;
  738. iDstTop = rect.bottom;
  739. iSrcTop = abs(pbih->biHeight);
  740. }
  741. iRetVal = StretchDIBits(hdc, rect.left, iDstTop, rect.right - rect.left, iDstHt,
  742. 0, iSrcTop, pbih->biWidth, pbih->biHeight,
  743. pScaledBits, pbiScaled, DIB_RGB_COLORS, SRCCOPY);
  744. SelectObject(hdc, hOldBrush);
  745. DeleteObject(hBrush);
  746. SelectObject(hdc, hOldPen);
  747. if (hpal)
  748. {
  749. SelectPalette(hdc, hpalOld, TRUE);
  750. RealizePalette(hdc);
  751. }
  752. SelectObject(hdc, hBmpOld);
  753. }
  754. DeleteDC(hdc);
  755. }
  756. if (hbmpDithered)
  757. {
  758. DeleteObject(hbmpDithered);
  759. }
  760. if (pDitheredInfo)
  761. {
  762. LocalFree(pDitheredInfo);
  763. }
  764. return (iRetVal != GDI_ERROR);
  765. }
  766. STDAPI_(BOOL) CreateSizedDIBSECTION(const SIZE *prgSize, DWORD dwClrDepth, HPALETTE hpal,
  767. const BITMAPINFO *pCurInfo, HBITMAP *phBmp, BITMAPINFO **ppBMI, void **ppBits)
  768. {
  769. *phBmp = NULL;
  770. HDC hdc = GetDC(NULL);
  771. if (hdc)
  772. {
  773. HDC hdcBmp = CreateCompatibleDC(hdc);
  774. if (hdcBmp)
  775. {
  776. struct {
  777. BITMAPINFOHEADER bi;
  778. DWORD ct[256];
  779. } dib;
  780. dib.bi.biSize = sizeof(dib.bi);
  781. dib.bi.biWidth = prgSize->cx;
  782. dib.bi.biHeight = prgSize->cy;
  783. dib.bi.biPlanes = 1;
  784. dib.bi.biBitCount = (WORD) dwClrDepth;
  785. dib.bi.biCompression = BI_RGB;
  786. dib.bi.biSizeImage = CalcImageSize(prgSize, dwClrDepth);
  787. dib.bi.biXPelsPerMeter = 0;
  788. dib.bi.biYPelsPerMeter = 0;
  789. dib.bi.biClrUsed = (dwClrDepth <= 8) ? (1 << dwClrDepth) : 0;
  790. dib.bi.biClrImportant = 0;
  791. HPALETTE hpalOld = NULL;
  792. BOOL bDIB = TRUE;
  793. if (dwClrDepth <= 8)
  794. {
  795. // if they passed us the old structure with colour info, and we are the same bit depth, then copy it...
  796. if (pCurInfo && pCurInfo->bmiHeader.biBitCount == dwClrDepth)
  797. {
  798. // use the passed in colour info to generate the DIBSECTION
  799. int iColours = pCurInfo->bmiHeader.biClrUsed;
  800. if (!iColours)
  801. {
  802. iColours = dib.bi.biClrUsed;
  803. }
  804. // copy the data accross...
  805. if (iColours <= 256)
  806. {
  807. CopyMemory(dib.ct, pCurInfo->bmiColors, sizeof(RGBQUAD) * iColours);
  808. }
  809. else
  810. {
  811. bDIB = FALSE;
  812. }
  813. }
  814. else
  815. {
  816. // need to get the right palette....
  817. hpalOld = SelectPalette(hdcBmp, hpal, TRUE);
  818. RealizePalette(hdcBmp);
  819. int n = GetPaletteEntries(hpal, 0, 256, (LPPALETTEENTRY)&dib.ct[0]);
  820. ASSERT(n >= (int) dib.bi.biClrUsed);
  821. // now convert the PALETTEENTRY to RGBQUAD
  822. for (int i = 0; i < (int)dib.bi.biClrUsed; i ++)
  823. {
  824. dib.ct[i] = RGB(GetBValue(dib.ct[i]),GetGValue(dib.ct[i]),GetRValue(dib.ct[i]));
  825. }
  826. }
  827. }
  828. if (bDIB)
  829. {
  830. void *pbits;
  831. *phBmp = CreateDIBSection(hdcBmp, (LPBITMAPINFO)&dib, DIB_RGB_COLORS, &pbits, NULL, 0);
  832. if (*phBmp)
  833. {
  834. if (ppBMI)
  835. {
  836. *ppBMI = (BITMAPINFO *)LocalAlloc(LPTR, sizeof(dib));
  837. if (*ppBMI)
  838. {
  839. CopyMemory(*ppBMI, &dib, sizeof(dib));
  840. }
  841. }
  842. if (ppBits)
  843. {
  844. *ppBits = pbits;
  845. }
  846. }
  847. }
  848. DeleteDC(hdcBmp);
  849. }
  850. ReleaseDC(NULL, hdc);
  851. }
  852. return (*phBmp != NULL);
  853. }
  854. STDAPI_(void *) CalcBitsOffsetInDIB(BITMAPINFO *pBMI)
  855. {
  856. int ncolors = pBMI->bmiHeader.biClrUsed;
  857. if (ncolors == 0 && pBMI->bmiHeader.biBitCount <= 8)
  858. ncolors = 1 << pBMI->bmiHeader.biBitCount;
  859. if (pBMI->bmiHeader.biBitCount == 16 ||
  860. pBMI->bmiHeader.biBitCount == 32)
  861. {
  862. if (pBMI->bmiHeader.biCompression == BI_BITFIELDS)
  863. {
  864. ncolors = 3;
  865. }
  866. }
  867. return (void *)((UCHAR *)&pBMI->bmiColors[0] + ncolors * sizeof(RGBQUAD));
  868. }