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.

680 lines
19 KiB

  1. /*************************************************************************/
  2. /* Copyright (C) 1999 Microsoft Corporation */
  3. /* File: capture.cpp */
  4. /* Description: Convert a captured DVD frame from YUV formats to RGB, */
  5. /* and save to file in various formats. */
  6. /* Author: phillu */
  7. /*************************************************************************/
  8. #include "stdafx.h"
  9. #include "MSWebDVD.h"
  10. #include "msdvd.h"
  11. #include <shlobj.h>
  12. #include "capture.h"
  13. HRESULT WriteBitmapDataToJPEGFile(char * filename, CaptureBitmapData *bm);
  14. HRESULT WriteBitmapDataToBMPFile(char * filename, CaptureBitmapData *bm);
  15. // YUV FourCC Formats (byte-swapped). We support a subset of them.
  16. // Ref: http://www.webartz.com/fourcc/
  17. // packed formats
  18. #define FourCC_IYU1 '1UYI'
  19. #define FourCC_IYU2 '2UYI'
  20. #define FourCC_UYVY 'YVYU' // supported
  21. #define FourCC_UYNV 'VNYU' // supported
  22. #define FourCC_cyuv 'vuyc'
  23. #define FourCC_YUY2 '2YUY' // supported
  24. #define FourCC_YUNV 'VNUY' // supported
  25. #define FourCC_YVYU 'UYVY' // supported
  26. #define FourCC_Y41P 'P14Y'
  27. #define FourCC_Y211 '112Y'
  28. #define FourCC_Y41T 'T14Y'
  29. #define FourCC_Y42T 'T24Y'
  30. #define FourCC_CLJR 'RJLC'
  31. // planar formats
  32. #define FourCC_YVU9 '9UVY'
  33. #define FourCC_IF09 '90FI'
  34. #define FourCC_YV12 '21VY' // supported
  35. #define FourCC_I420 '024I'
  36. #define FourCC_IYUV 'VUYI'
  37. #define FourCC_CLPL 'LPLC'
  38. extern CComModule _Module;
  39. //
  40. // Save image file
  41. //
  42. static HRESULT
  43. SaveFileDialog(HWND hwnd, CaptureBitmapData *bmpdata)
  44. {
  45. USES_CONVERSION;
  46. HRESULT hr = S_OK;
  47. OPENFILENAME ofn;
  48. TCHAR filename[MAX_PATH];
  49. TCHAR FolderPath[MAX_PATH];
  50. const ciBufSize = 256;
  51. TCHAR titlestring[ciBufSize];
  52. // get the path of "My Pictures" and use it as default location
  53. if (SHGetSpecialFolderPath(NULL, FolderPath, CSIDL_MYPICTURES, FALSE) == FALSE)
  54. {
  55. // if My Pictures doesn't exist, try My Documents
  56. if (SHGetSpecialFolderPath(NULL, FolderPath, CSIDL_PERSONAL, FALSE) == FALSE)
  57. {
  58. // use current directory as last resort
  59. lstrcpyn(FolderPath, _T("."), sizeof(FolderPath) / sizeof(FolderPath[0]));
  60. }
  61. }
  62. ZeroMemory(&ofn, sizeof(ofn));
  63. ofn.lStructSize = sizeof(ofn);
  64. ofn.hwndOwner = hwnd;
  65. ofn.hInstance = _Module.m_hInstResource;
  66. ofn.lpstrFile = filename;
  67. ofn.lpstrDefExt = _T("jpg"); // it appears it doesn't matter what string to use
  68. // it will use the ext in lpstrFilter according to selected type.
  69. ofn.nMaxFile = MAX_PATH;
  70. ::LoadString(_Module.m_hInstResource, IDS_SAVE_FILE, titlestring, ciBufSize);
  71. ofn.lpstrTitle = titlestring;
  72. ofn.lpstrInitialDir = FolderPath;
  73. ofn.Flags = OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT | OFN_EXPLORER;
  74. lstrcpyn(filename, _T("capture"), sizeof(filename) / sizeof(filename[0]));
  75. // Make up the file type filter string
  76. TCHAR* filter = _T("JPEG\0*.JPG\0Windows Bitmap\0*.BMP\0");
  77. ofn.lpstrFilter = filter;
  78. ofn.nFilterIndex = 1; // set format to JPG as default
  79. // Present the file/save dialog
  80. if (GetSaveFileName(&ofn))
  81. {
  82. switch (ofn.nFilterIndex)
  83. {
  84. case 2:
  85. hr = WriteBitmapDataToBMPFile(T2A(filename), bmpdata);
  86. break;
  87. default:
  88. hr = WriteBitmapDataToJPEGFile(T2A(filename), bmpdata);
  89. break;
  90. }
  91. }
  92. return hr;
  93. }
  94. ///////////////////////////////////////////////////////////////////////
  95. // This block of code deals with converting YUV format to RGB bitmap
  96. ///////////////////////////////////////////////////////////////////////
  97. static inline BYTE Clamp(float x)
  98. {
  99. if (x < 0.0f)
  100. return 0;
  101. else if (x > 255.0f)
  102. return 255;
  103. else
  104. return (BYTE)(x + 0.5f);
  105. }
  106. // Convert YUV to RGB
  107. static inline void ConvertPixelToRGB(int y, int u, int v, BYTE *pBuf)
  108. {
  109. //
  110. // This equation was taken from Video Demystified (2nd Edition)
  111. // by Keith Jack, page 43.
  112. //
  113. BYTE red = Clamp((1.1644f * (y-16)) + (1.5960f * (v-128)) );
  114. BYTE grn = Clamp((1.1644f * (y-16)) - (0.8150f * (v-128)) - (0.3912f * (u-128)));
  115. BYTE blu = Clamp((1.1644f * (y-16)) + (2.0140f * (u-128)));
  116. // RGB format, 3 bytes per pixel
  117. pBuf[0] = red;
  118. pBuf[1] = grn;
  119. pBuf[2] = blu;
  120. }
  121. // Convert image in YUY2 format to RGB bitmap
  122. static void ConvertYUY2ToBitmap(YUV_IMAGE* lpImage, CaptureBitmapData* bmpdata)
  123. {
  124. long y, x;
  125. BYTE *pYUVBits;
  126. BYTE *pRGB;
  127. for (y = 0; y < lpImage->lHeight; y++)
  128. {
  129. pYUVBits = (BYTE *)lpImage + sizeof(YUV_IMAGE) + y * lpImage->lStride;
  130. pRGB = (BYTE *)(bmpdata->Scan0) + y * bmpdata->Stride;
  131. for (x = 0; x < lpImage->lWidth; x += 2)
  132. {
  133. int Y0 = (int) *pYUVBits++;
  134. int U0 = (int) *pYUVBits++;
  135. int Y1 = (int) *pYUVBits++;
  136. int V0 = (int) *pYUVBits++;
  137. ConvertPixelToRGB(Y0, U0, V0, pRGB);
  138. pRGB += BYTES_PER_PIXEL;
  139. ConvertPixelToRGB(Y1, U0, V0, pRGB);
  140. pRGB += BYTES_PER_PIXEL;
  141. }
  142. }
  143. }
  144. // Convert image in UYVY format to RGB bitmap
  145. static void ConvertUYVYToBitmap(YUV_IMAGE* lpImage, CaptureBitmapData* bmpdata)
  146. {
  147. long y, x;
  148. BYTE *pYUVBits;
  149. BYTE *pRGB;
  150. for (y = 0; y < lpImage->lHeight; y++)
  151. {
  152. pYUVBits = (BYTE *)lpImage + sizeof(YUV_IMAGE) + y * lpImage->lStride;
  153. pRGB = (BYTE *)(bmpdata->Scan0) + y * bmpdata->Stride;
  154. for (x = 0; x < lpImage->lWidth; x += 2)
  155. {
  156. int U0 = (int) *pYUVBits++;
  157. int Y0 = (int) *pYUVBits++;
  158. int V0 = (int) *pYUVBits++;
  159. int Y1 = (int) *pYUVBits++;
  160. ConvertPixelToRGB(Y0, U0, V0, pRGB);
  161. pRGB += BYTES_PER_PIXEL;
  162. ConvertPixelToRGB(Y1, U0, V0, pRGB);
  163. pRGB += BYTES_PER_PIXEL;
  164. }
  165. }
  166. }
  167. // Convert image in YVYU format to RGB bitmap
  168. static void ConvertYVYUToBitmap(YUV_IMAGE* lpImage, CaptureBitmapData* bmpdata)
  169. {
  170. long y, x;
  171. BYTE *pYUVBits;
  172. BYTE *pRGB;
  173. for (y = 0; y < lpImage->lHeight; y++)
  174. {
  175. pYUVBits = (BYTE *)lpImage + sizeof(YUV_IMAGE) + y * lpImage->lStride;
  176. pRGB = (BYTE *)(bmpdata->Scan0) + y * bmpdata->Stride;
  177. for (x = 0; x < lpImage->lWidth; x += 2)
  178. {
  179. int Y0 = (int) *pYUVBits++;
  180. int V0 = (int) *pYUVBits++;
  181. int Y1 = (int) *pYUVBits++;
  182. int U0 = (int) *pYUVBits++;
  183. ConvertPixelToRGB(Y0, U0, V0, pRGB);
  184. pRGB += BYTES_PER_PIXEL;
  185. ConvertPixelToRGB(Y1, U0, V0, pRGB);
  186. pRGB += BYTES_PER_PIXEL;
  187. }
  188. }
  189. }
  190. // Convert image in YV12 format to RGB bitmap
  191. static void ConvertYV12ToBitmap(YUV_IMAGE* lpImage, CaptureBitmapData* bmpdata)
  192. {
  193. long y, x;
  194. BYTE *pYBits;
  195. BYTE *pUBits;
  196. BYTE *pVBits;
  197. BYTE *pRGB;
  198. for (y = 0; y < lpImage->lHeight; y++)
  199. {
  200. pYBits = (BYTE *)lpImage + sizeof(YUV_IMAGE) + y * lpImage->lStride;
  201. pVBits = (BYTE *)lpImage + sizeof(YUV_IMAGE) + lpImage->lHeight * lpImage->lStride
  202. + (y/2) * (lpImage->lStride/2);
  203. pUBits = (BYTE *)lpImage + sizeof(YUV_IMAGE) + lpImage->lHeight * lpImage->lStride
  204. + ((lpImage->lHeight + y)/2) * (lpImage->lStride/2);
  205. pRGB = (BYTE *)(bmpdata->Scan0) + y * bmpdata->Stride;
  206. for (x = 0; x < lpImage->lWidth; x ++)
  207. {
  208. int Y0 = (int) *pYBits++;
  209. int V0 = (int) *pVBits;
  210. int U0 = (int) *pUBits;
  211. // U, V are shared by 2x2 pixels. only advance pointers every two pixels
  212. if (x&1)
  213. {
  214. pVBits++;
  215. pUBits++;
  216. }
  217. ConvertPixelToRGB(Y0, U0, V0, pRGB);
  218. pRGB += BYTES_PER_PIXEL;
  219. }
  220. }
  221. }
  222. // Convert image in YVU9 format to RGB bitmap
  223. static void ConvertYVU9ToBitmap(YUV_IMAGE* lpImage, CaptureBitmapData* bmpdata)
  224. {
  225. long y, x;
  226. BYTE *pYBits;
  227. BYTE *pUBits;
  228. BYTE *pVBits;
  229. BYTE *pRGB;
  230. for (y = 0; y < lpImage->lHeight; y++)
  231. {
  232. pYBits = (BYTE *)lpImage + sizeof(YUV_IMAGE) + y * lpImage->lStride;
  233. pVBits = (BYTE *)lpImage + sizeof(YUV_IMAGE) + lpImage->lHeight * lpImage->lStride
  234. + (y/4) * (lpImage->lStride/4);
  235. pUBits = (BYTE *)lpImage + sizeof(YUV_IMAGE) + lpImage->lHeight * lpImage->lStride
  236. + ((lpImage->lHeight + y)/4) * (lpImage->lStride/4);
  237. pRGB = (BYTE *)(bmpdata->Scan0) + y * bmpdata->Stride;
  238. for (x = 0; x < lpImage->lWidth; x ++)
  239. {
  240. int Y0 = (int) *pYBits++;
  241. int V0 = (int) *pVBits;
  242. int U0 = (int) *pUBits;
  243. // U, V are shared by 4x4 pixels. only advance pointers every 4 pixels
  244. if ((x&3) == 3)
  245. {
  246. pVBits++;
  247. pUBits++;
  248. }
  249. ConvertPixelToRGB(Y0, U0, V0, pRGB);
  250. pRGB += BYTES_PER_PIXEL;
  251. }
  252. }
  253. }
  254. static HRESULT InitBitmapData(CaptureBitmapData *bmpdata, int Width, int Height)
  255. {
  256. bmpdata->Width = Width;
  257. bmpdata->Height = Height;
  258. bmpdata->Stride = (BYTES_PER_PIXEL*Width + 3) & (~3); // align with word boundary
  259. bmpdata->Scan0 = new BYTE[Height * bmpdata->Stride];
  260. bmpdata->pBuffer = bmpdata->Scan0;
  261. if (NULL == bmpdata->Scan0)
  262. {
  263. return E_OUTOFMEMORY;
  264. }
  265. return S_OK;
  266. }
  267. static void FreeBitmapData(CaptureBitmapData *bmpdata)
  268. {
  269. delete[] bmpdata->pBuffer;
  270. bmpdata->pBuffer = NULL;
  271. bmpdata->Scan0 = NULL;
  272. }
  273. static HRESULT ConvertToBitmapImage(YUV_IMAGE *lpImage, CaptureBitmapData *bmp)
  274. {
  275. HRESULT hr = S_OK;
  276. // create a bitmap object
  277. hr = InitBitmapData(bmp, lpImage->lWidth, lpImage->lHeight);
  278. if (FAILED(hr))
  279. {
  280. return hr;
  281. }
  282. bool fSupported = true;
  283. // convert different types of YUV formats to RGB
  284. switch (lpImage->dwFourCC)
  285. {
  286. case FourCC_YUY2:
  287. case FourCC_YUNV: // the two are equivalent
  288. ConvertYUY2ToBitmap(lpImage, bmp);
  289. break;
  290. case FourCC_UYVY:
  291. case FourCC_UYNV: // equivalent
  292. ConvertUYVYToBitmap(lpImage, bmp);
  293. break;
  294. case FourCC_YVYU:
  295. ConvertYVYUToBitmap(lpImage, bmp);
  296. break;
  297. case FourCC_YV12:
  298. ConvertYV12ToBitmap(lpImage, bmp);
  299. break;
  300. case FourCC_YVU9:
  301. ConvertYVU9ToBitmap(lpImage, bmp);
  302. break;
  303. default:
  304. fSupported = false;
  305. break;
  306. }
  307. if (!fSupported)
  308. {
  309. hr = E_FORMAT_NOT_SUPPORTED;
  310. }
  311. return hr;
  312. }
  313. #ifdef _DEBUG
  314. static void AlertUnsupportedFormat(DWORD dwFourCC, HWND hwnd)
  315. {
  316. char buf[256];
  317. StringCchPrintf(buf, sizeof(buf), "YUV format %c%c%c%c not supported\n",
  318. dwFourCC & 0xff,
  319. (dwFourCC >> 8) & 0xff,
  320. (dwFourCC >> 16) & 0xff,
  321. (dwFourCC >> 24) & 0xff);
  322. MessageBoxA(hwnd, buf, "", MB_OK);
  323. }
  324. #endif
  325. // This helper function does several things.
  326. //
  327. // First, it determines if clipping is necessary, return true if it is,
  328. // and false otherwise.
  329. //
  330. // Second, it maps the ViewClipRect (clipping rect in the view coordinates,
  331. // i.e. the one after correcting aspect ratio) back to the raw captured
  332. // image coordinates. Return it in ImageClipRect. This step is skipped (and
  333. // ImageClipRect will be invalid) if clipping is not necessary.
  334. //
  335. // Third, it calculates the stretched image size. It should be in the same
  336. // aspect ratio as the ViewClipRect. It will also be made as full-size as possible
  337. static bool ClipAndStretchSizes(YUV_IMAGE *lpImage, const RECT *pViewClipRect,
  338. RECT *pImageClipRect, int *pViewWidth, int *pViewHeight)
  339. {
  340. float aspectRaw = (float)lpImage->lHeight / (float)lpImage->lWidth;
  341. float aspectView = (float)lpImage->lAspectY / (float)lpImage->lAspectX;
  342. int viewWidth = lpImage->lWidth;
  343. int viewHeight = (int)(viewWidth * aspectView + 0.5f);
  344. // the rect is given in the stretched (aspect-ratio corrected) window
  345. // we will adjust it back to the raw image space
  346. bool fClip = false;
  347. if (pViewClipRect)
  348. {
  349. RECT rc;
  350. rc.left = pViewClipRect->left;
  351. rc.right = pViewClipRect->right;
  352. rc.top = (int)(pViewClipRect->top * aspectRaw / aspectView + 0.5f);
  353. rc.bottom = (int)(pViewClipRect->bottom * aspectRaw / aspectView + 0.5f);
  354. RECT rcFullImage;
  355. ::SetRect(&rcFullImage, 0, 0, lpImage->lWidth, lpImage->lHeight);
  356. if (! ::EqualRect(&rc, &rcFullImage) &&
  357. ::IntersectRect(pImageClipRect, &rc, &rcFullImage))
  358. {
  359. fClip = true;
  360. }
  361. }
  362. // adjust the stretched image size according to the rect aspect ratio
  363. if (fClip)
  364. {
  365. float aspectRect = (float)(RECTHEIGHT(pViewClipRect))
  366. / (float)(RECTWIDTH(pViewClipRect));
  367. if (aspectRect < aspectView)
  368. {
  369. // clip rect has a wider aspect ratio.
  370. // keep the width, adjust the height
  371. viewHeight = (int)(viewWidth * aspectRect + 0.5f);
  372. }
  373. else
  374. {
  375. // clip rect has a taller aspect ratio.
  376. // keep the height, adjust width
  377. viewWidth = (int)(viewHeight / aspectRect + 0.5f);
  378. }
  379. }
  380. *pViewWidth = viewWidth;
  381. *pViewHeight = viewHeight;
  382. return fClip;
  383. }
  384. static HRESULT ClipBitmap(CaptureBitmapData *bmpdata, RECT *rect)
  385. {
  386. HRESULT hr = S_OK;
  387. if (NULL == rect)
  388. {
  389. return S_OK;
  390. }
  391. bmpdata->Width = rect->right - rect->left;
  392. bmpdata->Height = rect->bottom - rect->top;
  393. // bmpdata->Stride = bmpdata->Stride;
  394. bmpdata->Scan0 = bmpdata->Scan0 +
  395. rect->top * bmpdata->Stride + (rect->left * BYTES_PER_PIXEL);
  396. return S_OK;
  397. }
  398. static HRESULT StretchBitmap(CaptureBitmapData *bmpdata, int newWidth, int newHeight)
  399. {
  400. HRESULT hr = S_OK;
  401. int nX, nY, nX0, nY0, nX1, nY1;
  402. double dXRatio, dYRatio, dXCoor, dYCoor, dXR, dYR;
  403. double pdRGB0[3];
  404. double pdRGB1[3];
  405. BYTE *pRow0;
  406. BYTE *pRow1;
  407. BYTE *pPix0;
  408. BYTE *pPix1;
  409. BYTE *pDest;
  410. if (bmpdata->Width == newWidth && bmpdata->Height == newHeight)
  411. {
  412. return hr;
  413. }
  414. int newStride = (newWidth*BYTES_PER_PIXEL + 3) & (~3); // align with word boundary
  415. BYTE *pBuffer = new BYTE[newHeight * newStride];
  416. if (NULL == pBuffer)
  417. {
  418. return E_OUTOFMEMORY;
  419. }
  420. dXRatio = (double)(bmpdata->Width)/(double)(newWidth);
  421. dYRatio = (double)(bmpdata->Height)/(double)(newHeight);
  422. // bilinear stretching
  423. // Note this is not the most efficient algorithm as it uses a lot of floating calc
  424. // Nevertheless it is simple
  425. for (nY = 0; nY < newHeight; nY++)
  426. {
  427. // determine two coordinates along Y direction for interpolation
  428. dYCoor = (nY + 0.5)*dYRatio - 0.5;
  429. if (dYCoor < 0)
  430. {
  431. nY0 = nY1 = 0;
  432. dYR = 0.0;
  433. }
  434. else if (dYCoor >= bmpdata->Height - 1)
  435. {
  436. nY0 = nY1 = bmpdata->Height - 1;
  437. dYR = 0.0;
  438. }
  439. else
  440. {
  441. nY0 = (int)dYCoor;
  442. nY1 = nY0 + 1;
  443. dYR = dYCoor - nY0;
  444. }
  445. pRow0 = bmpdata->Scan0 + nY0 * bmpdata->Stride;
  446. pRow1 = bmpdata->Scan0 + nY1 * bmpdata->Stride;
  447. pDest = pBuffer + nY * newStride;
  448. for (nX = 0; nX < newWidth; nX++, pDest+=3)
  449. {
  450. // determine two coordinates along X direction for interpolation
  451. dXCoor = (nX + 0.5)*dXRatio - 0.5;
  452. if (dXCoor < 0)
  453. {
  454. nX0 = nX1 = 0;
  455. dXR = 0.0;
  456. }
  457. else if (dXCoor >= bmpdata->Width - 1)
  458. {
  459. nX0 = nX1 = bmpdata->Width - 1;
  460. dXR = 0.0;
  461. }
  462. else
  463. {
  464. nX0 = (int)dXCoor;
  465. nX1 = nX0 + 1;
  466. dXR = dXCoor - nX0;
  467. }
  468. // interpolate along X, in the upper row
  469. pPix0 = pRow0 + nX0 * BYTES_PER_PIXEL;
  470. pPix1 = pRow0 + nX1 * BYTES_PER_PIXEL;
  471. pdRGB0[0] = pPix0[0] + (pPix1[0] - pPix0[0])*dXR;
  472. pdRGB0[1] = pPix0[1] + (pPix1[1] - pPix0[1])*dXR;
  473. pdRGB0[2] = pPix0[2] + (pPix1[2] - pPix0[2])*dXR;
  474. // interpolate along X, in the lower row
  475. pPix0 = pRow1 + nX0 * BYTES_PER_PIXEL;
  476. pPix1 = pRow1 + nX1 * BYTES_PER_PIXEL;
  477. pdRGB1[0] = pPix0[0] + (pPix1[0] - pPix0[0])*dXR;
  478. pdRGB1[1] = pPix0[1] + (pPix1[1] - pPix0[1])*dXR;
  479. pdRGB1[2] = pPix0[2] + (pPix1[2] - pPix0[2])*dXR;
  480. // interpolate along Y
  481. pDest[0] = (BYTE)(pdRGB0[0] + (pdRGB1[0] - pdRGB0[0])*dYR + 0.5);
  482. pDest[1] = (BYTE)(pdRGB0[1] + (pdRGB1[1] - pdRGB0[1])*dYR + 0.5);
  483. pDest[2] = (BYTE)(pdRGB0[2] + (pdRGB1[2] - pdRGB0[2])*dYR + 0.5);
  484. }
  485. }
  486. // replace the bitmap buffer
  487. delete[] bmpdata->pBuffer;
  488. bmpdata->pBuffer = bmpdata->Scan0 = pBuffer;
  489. bmpdata->Stride = newStride;
  490. bmpdata->Width = newWidth;
  491. bmpdata->Height = newHeight;
  492. return hr;
  493. }
  494. /////////////////////////////////////////////////////////////////////////////
  495. //
  496. // ConvertImageAndSave: this is the main function to be called by the player.
  497. //
  498. // Convert a captured YUV image to a GDI BitmapImage, and save it to a file
  499. // allowing user to choose file format and file name.
  500. // The clipping rectangle should be in the full size view coordinate system
  501. // with corrected aspect ratio (i.e. 720x540 for 4:3).
  502. HRESULT ConvertImageAndSave(YUV_IMAGE *lpImage, RECT *pViewClipRect, HWND hwnd)
  503. {
  504. HRESULT hr = S_OK;
  505. CaptureBitmapData bmpdata;
  506. hr = ConvertToBitmapImage(lpImage, &bmpdata);
  507. #ifdef _DEBUG
  508. if (E_FORMAT_NOT_SUPPORTED == hr)
  509. {
  510. AlertUnsupportedFormat(lpImage->dwFourCC, hwnd);
  511. }
  512. #endif
  513. // calculate size and rectangles for clipping and stretching
  514. int viewWidth, viewHeight; // size of the clipped and stretch image
  515. bool fClip; // is clipping necessary
  516. RECT rcClipImage; // view clipping rect mapped to image space
  517. fClip = ClipAndStretchSizes(lpImage, pViewClipRect, &rcClipImage,
  518. &viewWidth, &viewHeight);
  519. // crop the image to the clip rectangle.
  520. if (SUCCEEDED(hr) && fClip)
  521. {
  522. hr = ClipBitmap(&bmpdata, &rcClipImage);
  523. }
  524. // stretch the image to the right aspect ratio
  525. if (SUCCEEDED(hr))
  526. {
  527. hr = StretchBitmap(&bmpdata, viewWidth, viewHeight);
  528. }
  529. // save final bitmap to a file
  530. if (SUCCEEDED(hr))
  531. {
  532. hr = SaveFileDialog(hwnd, &bmpdata);
  533. }
  534. // clean up, release the image buffer
  535. FreeBitmapData(&bmpdata);
  536. return hr;
  537. }