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.

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