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.

2526 lines
81 KiB

  1. /*****************************************************************************
  2. *
  3. * (C) COPYRIGHT MICROSOFT CORPORATION, 2000
  4. *
  5. * TITLE: item.cpp
  6. *
  7. * VERSION: 1.0
  8. *
  9. * AUTHOR: RickTu
  10. *
  11. * DATE: 10/18/00
  12. *
  13. * DESCRIPTION: Implements an item class that encapsulates the photos
  14. * we are dealing with.
  15. *
  16. *****************************************************************************/
  17. #include <precomp.h>
  18. #pragma hdrstop
  19. /*****************************************************************************
  20. _ScaleImage
  21. Scales src rect to fit into dest rect while preserving aspect ratio
  22. *****************************************************************************/
  23. HRESULT _ScaleImage( Gdiplus::Rect * pSrc, Gdiplus::Rect * pDest )
  24. {
  25. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("_ScaleImage()")));
  26. if (!pDest || !pSrc)
  27. {
  28. WIA_ERROR((TEXT("_ScaleImage: bad params, exiting early!")));
  29. return E_INVALIDARG;
  30. }
  31. WIA_TRACE((TEXT("_ScaleImage: src before scaling: (%d, %d) @ (%d, %d)"), pSrc->Width, pSrc->Height, pSrc->X, pSrc->Y));
  32. //
  33. // Scale without any crop
  34. //
  35. SIZE sizeNew;
  36. INT NewX = pDest->X, NewY = pDest->Y;
  37. WIA_TRACE((TEXT("_ScaleImage: dest before scaling: (%d, %d) @ (%d, %d)"),pDest->Width, pDest->Height, pDest->X, pDest->Y));
  38. sizeNew = PrintScanUtil::ScalePreserveAspectRatio( pDest->Width, pDest->Height, pSrc->Width, pSrc->Height );
  39. NewX += ((pDest->Width - sizeNew.cx) / 2);
  40. NewY += ((pDest->Height - sizeNew.cy) / 2);
  41. pDest->X = NewX;
  42. pDest->Y = NewY;
  43. pDest->Width = sizeNew.cx;
  44. pDest->Height = sizeNew.cy;
  45. WIA_TRACE((TEXT("_ScaleImage: dest after scaling: (%d, %d) @ (%d, %d)"),pDest->Width, pDest->Height, pDest->X, pDest->Y));
  46. return S_OK;
  47. }
  48. /*****************************************************************************
  49. _CropImage
  50. Scales src rect to fit into dest rect while preserving aspect ratio
  51. *****************************************************************************/
  52. HRESULT _CropImage( Gdiplus::Rect * pSrc, Gdiplus::Rect * pDest )
  53. {
  54. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("_CropImage()")));
  55. if (!pDest || !pSrc)
  56. {
  57. WIA_ERROR((TEXT("_CropImage: bad params, exiting early!")));
  58. return E_INVALIDARG;
  59. }
  60. WIA_TRACE((TEXT("_CropImage: pDest before cropping: (%d, %d) @ (%d, %d)"), pDest->Width, pDest->Height, pDest->X, pDest->Y));
  61. //
  62. // Scale without any crop
  63. //
  64. SIZE sizeNew;
  65. INT NewX = pSrc->X, NewY = pSrc->Y;
  66. WIA_TRACE((TEXT("_CropImage: pSrc before cropping: (%d, %d) @ (%d, %d)"),pSrc->Width, pSrc->Height, pSrc->X, pSrc->Y));
  67. sizeNew = PrintScanUtil::ScalePreserveAspectRatio( pSrc->Width, pSrc->Height, pDest->Width, pDest->Height );
  68. NewX += ((pSrc->Width - sizeNew.cx) / 2);
  69. NewY += ((pSrc->Height - sizeNew.cy) / 2);
  70. pSrc->X = NewX;
  71. pSrc->Y = NewY;
  72. pSrc->Width = sizeNew.cx;
  73. pSrc->Height = sizeNew.cy;
  74. WIA_TRACE((TEXT("_CropImage: pSrc after cropping: (%d, %d) @ (%d, %d)"),pSrc->Width, pSrc->Height, pSrc->X, pSrc->Y));
  75. return S_OK;
  76. }
  77. /*****************************************************************************
  78. _GetImageDimensions
  79. Given a GDI+ image object, return the dimensions in the given
  80. rectangle...
  81. *****************************************************************************/
  82. HRESULT _GetImageDimensions( Gdiplus::Image * pImage, Gdiplus::RectF &rect, Gdiplus::REAL &scalingFactorForY )
  83. {
  84. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("_GetImageDimensions()")));
  85. if (!pImage)
  86. {
  87. WIA_ERROR((TEXT("_GetImageDimensions: bad params, exiting early!")));
  88. return E_INVALIDARG;
  89. }
  90. Gdiplus::Unit Unit;
  91. HRESULT hr = Gdiplus2HRESULT( pImage->GetBounds( &rect, &Unit ) );
  92. if (FAILED(hr))
  93. {
  94. //
  95. // Try the old fashioned way...
  96. //
  97. rect.X = (Gdiplus::REAL)0.0;
  98. rect.Y = (Gdiplus::REAL)0.0;
  99. rect.Width = (Gdiplus::REAL)pImage->GetWidth();
  100. hr = Gdiplus2HRESULT( pImage->GetLastStatus() );
  101. WIA_CHECK_HR(hr,"_GetImageDimensions: GetWidth failed!");
  102. if (SUCCEEDED(hr))
  103. {
  104. rect.Height = (Gdiplus::REAL)pImage->GetHeight();
  105. hr = Gdiplus2HRESULT( pImage->GetLastStatus() );
  106. WIA_CHECK_HR(hr,"_GetImageDimensions: GetHeight failed!");
  107. }
  108. }
  109. else
  110. {
  111. if (Unit != Gdiplus::UnitPixel)
  112. {
  113. hr = S_FALSE;
  114. }
  115. }
  116. Gdiplus::REAL xDPI = pImage->GetHorizontalResolution();
  117. Gdiplus::REAL yDPI = pImage->GetVerticalResolution();
  118. if (yDPI)
  119. {
  120. scalingFactorForY = xDPI / yDPI;
  121. }
  122. else
  123. {
  124. scalingFactorForY = (Gdiplus::REAL)1.0;
  125. }
  126. WIA_RETURN_HR(hr);
  127. }
  128. /*****************************************************************************
  129. CPhotoItem -- constructors/desctructor
  130. <Notes>
  131. *****************************************************************************/
  132. CPhotoItem::CPhotoItem( LPITEMIDLIST pidlFull )
  133. : _pidlFull(NULL),
  134. _pImage(NULL),
  135. _lFrameCount(-1),
  136. _bTimeFrames(FALSE),
  137. _pAnnotations(NULL),
  138. _pAnnotBits(NULL),
  139. _bWeKnowAnnotationsDontExist(FALSE),
  140. _pThumbnails(NULL),
  141. _cRef(0),
  142. _llFileSize(0),
  143. _uImageType(DontKnowImageType),
  144. _DPIx((Gdiplus::REAL)0.0),
  145. _DPIy((Gdiplus::REAL)0.0)
  146. {
  147. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM, TEXT("CPhotoItem::CPhotoItem( fully qualified pidl )")));
  148. if (pidlFull)
  149. {
  150. _pidlFull = ILClone( pidlFull );
  151. WIA_TRACE((TEXT("_pidlFull = 0x%x"),_pidlFull));
  152. }
  153. *_szFileName = 0;
  154. //
  155. // Get just file name from the pidl
  156. //
  157. SHFILEINFO fi = {0};
  158. if (SHGetFileInfo( (LPCTSTR)pidlFull, 0, &fi, sizeof(fi), SHGFI_DISPLAYNAME| SHGFI_PIDL ))
  159. {
  160. lstrcpyn( _szFileName, fi.szDisplayName, ARRAYSIZE(_szFileName) );
  161. }
  162. }
  163. CPhotoItem::~CPhotoItem()
  164. {
  165. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM, TEXT("CPhotoItem::~CPhotoItem()")));
  166. CAutoCriticalSection lock( _csItem );
  167. //
  168. // Free pidl for item
  169. //
  170. if (_pidlFull)
  171. {
  172. WIA_TRACE((TEXT("_pidlFull = 0x%x"),_pidlFull));
  173. ILFree( _pidlFull );
  174. _pidlFull = NULL;
  175. }
  176. //
  177. // Free GDI+ icon
  178. //
  179. if (_pClassBitmap)
  180. {
  181. delete _pClassBitmap;
  182. _pClassBitmap = NULL;
  183. }
  184. //
  185. // Free bitmaps of thumbnails
  186. //
  187. if (_pThumbnails)
  188. {
  189. for (INT i=0; i < _lFrameCount; i++)
  190. {
  191. if (_pThumbnails[i])
  192. {
  193. DeleteObject( _pThumbnails[i] );
  194. }
  195. }
  196. delete _pThumbnails;
  197. _pThumbnails = NULL;
  198. }
  199. //
  200. // Destroy GDI+ backing images. This also destroys any
  201. // annotation data we have...
  202. //
  203. _DiscardGdiPlusImages();
  204. }
  205. /*****************************************************************************
  206. CPhotoItem IUnknown methods
  207. <Notes>
  208. *****************************************************************************/
  209. ULONG CPhotoItem::AddRef()
  210. {
  211. LONG l = InterlockedIncrement(&_cRef);
  212. WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS,TEXT("CPhotoItem(0x%x)::AddRef( new count is %d )"),this,l));
  213. if (l < 0)
  214. {
  215. return 0;
  216. }
  217. return (ULONG)l;
  218. }
  219. ULONG CPhotoItem::Release()
  220. {
  221. LONG l = InterlockedDecrement(&_cRef);
  222. WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS,TEXT("CPhotoItem(0x%x)::Release( new count is %d )"),this,l));
  223. if (l > 0)
  224. return (ULONG)l;
  225. WIA_TRACE((TEXT("deleting object ( this == 0x%x ) because ref count is zero."),this));
  226. delete this;
  227. return 0;
  228. }
  229. ULONG CPhotoItem::ReleaseWithoutDeleting()
  230. {
  231. LONG l = InterlockedDecrement(&_cRef);
  232. WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS,TEXT("CPhotoItem(0x%x)::Release( new count is %d )"),this,l));
  233. return (ULONG)l;
  234. }
  235. /*****************************************************************************
  236. CPhotoItem::GetImageFrameCount
  237. returns the number of frames (pages) in this image
  238. *****************************************************************************/
  239. HRESULT CPhotoItem::GetImageFrameCount(LONG * pFrameCount)
  240. {
  241. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::GetImageFrameCount(%s)"),_szFileName));
  242. if (!pFrameCount)
  243. {
  244. return E_INVALIDARG;
  245. }
  246. HRESULT hr = S_OK;
  247. //
  248. // Protect us as we go get info about the item...
  249. //
  250. CAutoCriticalSection lock(_csItem);
  251. if (_lFrameCount == -1)
  252. {
  253. _lFrameCount = 1;
  254. //
  255. // Ensure the GDI+ image object has been created...this will also
  256. // update the frame count...
  257. //
  258. hr = _CreateGdiPlusImage();
  259. if (SUCCEEDED(hr) && _pImage)
  260. {
  261. LONG lPageFrames;
  262. LONG lTimeFrames;
  263. lPageFrames = _pImage->GetFrameCount(&Gdiplus::FrameDimensionPage);
  264. lTimeFrames = _pImage->GetFrameCount(&Gdiplus::FrameDimensionTime);
  265. if ((lPageFrames > 0) && (lTimeFrames <= 1))
  266. {
  267. _lFrameCount = lPageFrames;
  268. }
  269. else if (lTimeFrames > 0)
  270. {
  271. //
  272. // This is an animated GIF, report only 1 frame...
  273. //
  274. _lFrameCount = 1;
  275. _bTimeFrames = TRUE;
  276. }
  277. else
  278. {
  279. _lFrameCount = 1;
  280. }
  281. }
  282. }
  283. *pFrameCount = ((_lFrameCount == -1) ? 0 : _lFrameCount);
  284. WIA_TRACE((TEXT("%s: returning _FrameCount = %d"),_szFileName,*pFrameCount));
  285. return hr;
  286. }
  287. /*****************************************************************************
  288. CPhotoItem::GetClassBitmap
  289. Returns default icon for class (.jpg, .bmp, etc) for this item...
  290. *****************************************************************************/
  291. HBITMAP CPhotoItem::GetClassBitmap( const SIZE &sizeDesired )
  292. {
  293. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::GetClassBitmap( %s, size = %d,%d "),_szFileName,sizeDesired.cx, sizeDesired.cy ));
  294. HBITMAP hbmReturn = NULL;
  295. CAutoCriticalSection lock(_csItem);
  296. if (!_pClassBitmap)
  297. {
  298. //
  299. // Get icon from shell
  300. //
  301. SHFILEINFO fi = {0};
  302. if (SHGetFileInfo( (LPCTSTR)_pidlFull, 0, &fi, sizeof(fi), SHGFI_PIDL | SHGFI_SYSICONINDEX ))
  303. {
  304. //
  305. // Get large (48 x 48) icon image list
  306. //
  307. IImageList * piml = NULL;
  308. if (SUCCEEDED(SHGetImageList( SHIL_EXTRALARGE, IID_IImageList, (void **)&piml )) && piml)
  309. {
  310. HICON hIcon = NULL;
  311. if (SUCCEEDED(piml->GetIcon( fi.iIcon, 0, &hIcon )) && hIcon)
  312. {
  313. //
  314. // Got the ICON, create a bitmap for it...
  315. //
  316. hbmReturn = WiaUiUtil::CreateIconThumbnail( (HWND)NULL, 50, 60, hIcon, NULL );
  317. if (hbmReturn)
  318. {
  319. _pClassBitmap = new Gdiplus::Bitmap( hbmReturn, NULL );
  320. DeleteObject( hbmReturn );
  321. hbmReturn = NULL;
  322. }
  323. DestroyIcon( hIcon );
  324. }
  325. piml->Release();
  326. }
  327. }
  328. }
  329. if (_pClassBitmap)
  330. {
  331. SIZE sizeDrawSize = {0};
  332. //
  333. // Scale image to fill thumbnail space while preserving
  334. // aspect ratio...
  335. //
  336. sizeDrawSize = PrintScanUtil::ScalePreserveAspectRatio( sizeDesired.cx,
  337. sizeDesired.cy,
  338. _pClassBitmap->GetWidth(),
  339. _pClassBitmap->GetHeight()
  340. );
  341. WIA_TRACE((TEXT("CPhotoItem::GetClassBitmap(%s) - _pClassBitmap( %d, %d )"),_szFileName,_pClassBitmap->GetWidth(), _pClassBitmap->GetHeight()));
  342. WIA_TRACE((TEXT("CPhotoItem::GetClassBitmap(%s) - sizeDesired( %d, %d )"),_szFileName,sizeDesired.cx, sizeDesired.cy));
  343. WIA_TRACE((TEXT("CPhotoItem::GetClassBitmap(%s) - sizeDrawsize( %d, %d )"),_szFileName,sizeDrawSize.cx, sizeDrawSize.cy));
  344. Gdiplus::Bitmap * pImage = new Gdiplus::Bitmap( sizeDesired.cx, sizeDesired.cy );
  345. if (pImage)
  346. {
  347. HRESULT hr = Gdiplus2HRESULT(pImage->GetLastStatus());
  348. if (SUCCEEDED(hr))
  349. {
  350. //
  351. // Get a graphics to render to
  352. //
  353. Graphics *pGraphics = Gdiplus::Graphics::FromImage((Gdiplus::Image *)pImage);
  354. if (pGraphics)
  355. {
  356. hr = Gdiplus2HRESULT(pGraphics->GetLastStatus());
  357. //
  358. // Make sure it is valid
  359. //
  360. if (SUCCEEDED(hr))
  361. {
  362. //
  363. // erase the background of the image
  364. //
  365. pGraphics->Clear( g_wndColor );
  366. //
  367. // Set the interpolation mode to high quality
  368. //
  369. pGraphics->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBicubic );
  370. //
  371. // Set the smoothing (anti-aliasing) mode to high quality as well
  372. //
  373. pGraphics->SetSmoothingMode( Gdiplus::SmoothingModeHighQuality );
  374. //
  375. // Draw scaled image
  376. //
  377. WIA_TRACE((TEXT("CPhotoItem::GetClassBitmap(%s) - calling pGraphics->DrawImage( _pClassBitmap, %d, %d, %d, %d )"),_szFileName,0 + ((sizeDesired.cx - sizeDrawSize.cx) / 2),0 + ((sizeDesired.cy - sizeDrawSize.cy) / 2),sizeDrawSize.cx,sizeDrawSize.cy));
  378. hr = Gdiplus2HRESULT(pGraphics->DrawImage( _pClassBitmap,
  379. 0 + ((sizeDesired.cx - sizeDrawSize.cx) / 2),
  380. 0 + ((sizeDesired.cy - sizeDrawSize.cy) / 2),
  381. sizeDrawSize.cx,
  382. sizeDrawSize.cy
  383. ));
  384. WIA_CHECK_HR(hr,"CPhotoItem::GetClassBitmap() - pGraphics->DrawImage( _pClassBitmap ) failed!");
  385. if (SUCCEEDED(hr))
  386. {
  387. pImage->GetHBITMAP( g_wndColor, &hbmReturn );
  388. }
  389. }
  390. //
  391. // Clean up our dynamically allocated graphics
  392. //
  393. delete pGraphics;
  394. }
  395. else
  396. {
  397. WIA_ERROR((TEXT("CPhotoItem::GetClassBitmap(%s) - pGraphics was NULL!"),_szFileName));
  398. hr = E_OUTOFMEMORY;
  399. }
  400. }
  401. else
  402. {
  403. WIA_ERROR((TEXT("CPhotoItem::GetClassBitmap(%s) - pImage failed to be created, hr = 0x%x"),hr));
  404. }
  405. delete pImage;
  406. }
  407. }
  408. return hbmReturn;
  409. }
  410. /*****************************************************************************
  411. CPhotoItem::GetThumbnailBitmap
  412. Given a DC and a desired size, return an HBITMAP of the thumbnail
  413. for a this item. The caller MUST free the HBITMAP returned
  414. from this function.
  415. *****************************************************************************/
  416. HBITMAP CPhotoItem::GetThumbnailBitmap( const SIZE &sizeDesired, LONG lFrame )
  417. {
  418. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::GetThumbnailBitmap( %s, size = %d,%d "),_szFileName,sizeDesired.cx, sizeDesired.cy ));
  419. HBITMAP hbmReturn = NULL;
  420. Gdiplus::Image * pImageToUse = NULL;
  421. CAutoCriticalSection lock(_csItem);
  422. //
  423. // Make sure we have a thumbnail image for our photo...
  424. //
  425. _CreateGdiPlusThumbnail( sizeDesired, lFrame );
  426. if (_pThumbnails && (lFrame < _lFrameCount) && _pThumbnails[lFrame])
  427. {
  428. //
  429. // Use bitmap to draw with instead of going to the file...
  430. //
  431. pImageToUse = (Gdiplus::Image *)(Gdiplus::Bitmap::FromHBITMAP( _pThumbnails[lFrame], NULL ));
  432. }
  433. if (pImageToUse)
  434. {
  435. WIA_TRACE((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - pImageToUse is (%d x %d)"),_szFileName,pImageToUse->GetWidth(),pImageToUse->GetHeight()));
  436. Gdiplus::Bitmap * pImage = new Gdiplus::Bitmap( sizeDesired.cx, sizeDesired.cy );
  437. if (pImage)
  438. {
  439. HRESULT hr = Gdiplus2HRESULT(pImage->GetLastStatus());
  440. if (SUCCEEDED(hr))
  441. {
  442. //
  443. // Get a graphics to render to
  444. //
  445. Graphics *pGraphics = Gdiplus::Graphics::FromImage((Gdiplus::Image *)pImage);
  446. if (pGraphics)
  447. {
  448. hr = Gdiplus2HRESULT(pGraphics->GetLastStatus());
  449. //
  450. // Make sure it is valid
  451. //
  452. if (SUCCEEDED(hr))
  453. {
  454. //
  455. // compute how to scale the thumbnail image
  456. //
  457. SIZE sizeDrawSize = {0};
  458. sizeDrawSize = PrintScanUtil::ScalePreserveAspectRatio( sizeDesired.cx,
  459. sizeDesired.cy,
  460. pImageToUse->GetWidth(),
  461. pImageToUse->GetHeight()
  462. );
  463. //
  464. // erase the background of the image
  465. //
  466. pGraphics->Clear( g_wndColor );
  467. //
  468. // Set the interpolation mode to high quality
  469. //
  470. pGraphics->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBilinear );
  471. //
  472. // Draw scaled image
  473. //
  474. WIA_TRACE((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - calling pGraphics->DrawImage( pImageToUse, %d, %d, %d, %d )"),_szFileName,0 + ((sizeDesired.cx - sizeDrawSize.cx) / 2),0 + ((sizeDesired.cy - sizeDrawSize.cy) / 2),sizeDrawSize.cx,sizeDrawSize.cy));
  475. hr = Gdiplus2HRESULT(pGraphics->DrawImage( pImageToUse,
  476. 0 + ((sizeDesired.cx - sizeDrawSize.cx) / 2),
  477. 0 + ((sizeDesired.cy - sizeDrawSize.cy) / 2),
  478. sizeDrawSize.cx,
  479. sizeDrawSize.cy
  480. ));
  481. WIA_CHECK_HR(hr,"CPhotoItem::GetThumbnailBitmap() - pGraphics->DrawImage( pImageToUse ) failed!");
  482. if (SUCCEEDED(hr))
  483. {
  484. pImage->GetHBITMAP( g_wndColor, &hbmReturn );
  485. }
  486. }
  487. //
  488. // Clean up our dynamically allocated graphics
  489. //
  490. delete pGraphics;
  491. }
  492. else
  493. {
  494. WIA_ERROR((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - pGraphics was NULL!"),_szFileName));
  495. hr = E_OUTOFMEMORY;
  496. }
  497. }
  498. else
  499. {
  500. WIA_ERROR((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - pImage failed to be created, hr = 0x%x"),hr));
  501. }
  502. delete pImage;
  503. }
  504. //
  505. // If we created an image to wrap the bitmap bits, then delete it...
  506. //
  507. if (pImageToUse)
  508. {
  509. delete pImageToUse;
  510. }
  511. }
  512. else
  513. {
  514. WIA_ERROR((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - don't have stored thumbnail bitmap for this image!"),_szFileName));
  515. }
  516. return hbmReturn;
  517. }
  518. /*****************************************************************************
  519. CPhotoItem::_DoRotateAnnotations
  520. This function requires that the annotation data be already set up
  521. and initialized. This is true for the _pImage object as well. This
  522. function will not initialize on the fly.
  523. *****************************************************************************/
  524. HRESULT CPhotoItem::_DoRotateAnnotations( BOOL bClockwise, UINT Flags )
  525. {
  526. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_DoRotateAnnotations( %s, Flags = 0x%x )"),_szFileName,Flags));
  527. if (!_pAnnotations)
  528. {
  529. WIA_RETURN_HR(E_INVALIDARG);
  530. }
  531. if (!_pImage)
  532. {
  533. WIA_RETURN_HR(E_INVALIDARG);
  534. }
  535. HRESULT hr;
  536. Gdiplus::REAL scaleY;
  537. //
  538. // Get width & height of backing image...
  539. //
  540. Gdiplus::RectF rectBounds;
  541. hr = _GetImageDimensions( _pImage, rectBounds, scaleY );
  542. if (SUCCEEDED(hr))
  543. {
  544. INT i = 0;
  545. CAnnotation * pA = NULL;
  546. INT iNewW = 0, iNewH = 0;
  547. if ((Flags & RF_USE_THUMBNAIL_DATA) || (Flags & RF_USE_MEDIUM_QUALITY_DATA))
  548. {
  549. //
  550. // We flip here, because in the main _DoHandleRotation we only
  551. // rotated the thumbnail data, so the backing image width & height
  552. // haven't changed. It will be changed when we rotate to print, however,
  553. // so feed the correct values to the annotation rotate code...
  554. //
  555. iNewW = (INT)rectBounds.Height;
  556. iNewH = (INT)rectBounds.Width;
  557. }
  558. else
  559. {
  560. iNewW = (INT)rectBounds.Width;
  561. iNewH = (INT)rectBounds.Height;
  562. }
  563. WIA_TRACE((TEXT("CPhotoItem::_DoRotateAnnotations - bClockwise = %d, new width = %d, new height = %d"),bClockwise,iNewW,iNewH));
  564. //
  565. // rotate all the annotations
  566. //
  567. do
  568. {
  569. pA = _pAnnotations->GetAnnotation(i++);
  570. if (pA)
  571. {
  572. pA->Rotate( iNewW, iNewH, bClockwise );
  573. }
  574. } while( pA );
  575. }
  576. WIA_RETURN_HR(hr);
  577. }
  578. #define DO_CONVERT_GDIPLUS_STATUS(hr,status) if ( (status == Gdiplus::Ok) || \
  579. (status == Gdiplus::OutOfMemory) || \
  580. (status == Gdiplus::ObjectBusy) || \
  581. (status == Gdiplus::FileNotFound) || \
  582. (status == Gdiplus::AccessDenied) || \
  583. (status == Gdiplus::Win32Error) \
  584. ) \
  585. { \
  586. hr = Gdiplus2HRESULT( status ); \
  587. }\
  588. else \
  589. {\
  590. WIA_TRACE((TEXT("Mapping Gdiplus error %d to PPW_E_UNABLE_TO_ROTATE"),status));\
  591. hr = PPW_E_UNABLE_TO_ROTATE;\
  592. }
  593. /*****************************************************************************
  594. CPhotoItem::_DoHandleRotation
  595. Handle rotating the image to render if/when needed or specified...
  596. *****************************************************************************/
  597. HRESULT CPhotoItem::_DoHandleRotation( Gdiplus::Image * pImage, Gdiplus::Rect &src, Gdiplus::Rect * pDest, UINT Flags, Gdiplus::REAL &ScaleFactorForY )
  598. {
  599. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_DoHandleRotation( %s, Flags = 0x%x )"),_szFileName,Flags));
  600. HRESULT hr = S_OK;
  601. Gdiplus::GpStatus status = Gdiplus::Ok;
  602. if (Flags & RF_ROTATION_MASK)
  603. {
  604. WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - A rotation flag was specified"),_szFileName));
  605. if (Flags & RF_ROTATE_AS_NEEDED)
  606. {
  607. WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - RF_ROTATE_AS_NEEDED was specified"),_szFileName));
  608. //
  609. // If the source and destination aspect ratios are on the opposite sides of 1.0,
  610. // rotate the image 90 degrees
  611. //
  612. const DOUBLE srcAspect = (DOUBLE)src.Width / (DOUBLE)src.Height;
  613. const DOUBLE destAspect = (DOUBLE)pDest->Width / (DOUBLE)pDest->Height;
  614. if((srcAspect >= (DOUBLE)1.0) ^ (destAspect >= (DOUBLE)1.0))
  615. {
  616. //
  617. // Rotate the image as needed...
  618. //
  619. if (Flags & RF_ROTATE_270)
  620. {
  621. WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - Rotating Image 270 degrees"),_szFileName));
  622. status = pImage->RotateFlip( Gdiplus::Rotate270FlipNone );
  623. if (status == Gdiplus::Ok)
  624. {
  625. _DoRotateAnnotations( FALSE, Flags );
  626. }
  627. }
  628. else
  629. {
  630. WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - Rotating Image 90 degrees"),_szFileName));
  631. status = pImage->RotateFlip( Gdiplus::Rotate90FlipNone );
  632. if (status == Gdiplus::Ok)
  633. {
  634. _DoRotateAnnotations( TRUE, Flags );
  635. }
  636. }
  637. //
  638. // Map most of these error codes to UNABLE_TO_ROTATE...
  639. //
  640. DO_CONVERT_GDIPLUS_STATUS(hr,status)
  641. }
  642. }
  643. else
  644. {
  645. //
  646. // Rotate the image...
  647. //
  648. if (Flags & RF_ROTATE_90)
  649. {
  650. WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - Rotating Image 90 degrees"),_szFileName));
  651. status = pImage->RotateFlip( Gdiplus::Rotate90FlipNone );
  652. if (status == Gdiplus::Ok)
  653. {
  654. _DoRotateAnnotations( TRUE, Flags );
  655. }
  656. }
  657. else if (Flags & RF_ROTATE_180)
  658. {
  659. WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - Rotating Image 180 degrees"),_szFileName));
  660. status = pImage->RotateFlip( Gdiplus::Rotate180FlipNone );
  661. if (status == Gdiplus::Ok)
  662. {
  663. //
  664. // Rotate 90 degrees twice...
  665. //
  666. _DoRotateAnnotations( TRUE, Flags );
  667. _DoRotateAnnotations( TRUE, Flags );
  668. }
  669. }
  670. else if (Flags & RF_ROTATE_270)
  671. {
  672. WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - Rotating Image 270 degrees"),_szFileName));
  673. status = pImage->RotateFlip( Gdiplus::Rotate270FlipNone );
  674. if (status == Gdiplus::Ok)
  675. {
  676. _DoRotateAnnotations( FALSE, Flags );
  677. }
  678. }
  679. else
  680. {
  681. status = Gdiplus::Ok;
  682. }
  683. //
  684. // Map most of these error codes to UNABLE_TO_ROTATE...
  685. //
  686. DO_CONVERT_GDIPLUS_STATUS(hr,status);
  687. }
  688. //
  689. // If we were able to rotate the image, then update the source rectangle
  690. // to make sure it still reflects reality...
  691. //
  692. if (SUCCEEDED(hr))
  693. {
  694. Gdiplus::RectF rectBounds;
  695. hr = _GetImageDimensions( pImage, rectBounds, ScaleFactorForY );
  696. if (SUCCEEDED(hr))
  697. {
  698. src.Width = (INT)rectBounds.Width;
  699. src.Height = (INT)(rectBounds.Height * ScaleFactorForY);
  700. src.X = (INT)rectBounds.X;
  701. src.Y = (INT)(rectBounds.Y * ScaleFactorForY);
  702. }
  703. else
  704. {
  705. src.Width = 0;
  706. src.Height = 0;
  707. src.X = 0;
  708. src.Y = 0;
  709. }
  710. }
  711. }
  712. if (Flags & RF_NO_ERRORS_ON_FAILURE_TO_ROTATE)
  713. {
  714. WIA_RETURN_HR(S_OK);
  715. }
  716. WIA_RETURN_HR(hr);
  717. }
  718. /*****************************************************************************
  719. CPhotoItem::_RenderAnnotations
  720. If annotations exist, then render them on top of this image...
  721. *****************************************************************************/
  722. HRESULT CPhotoItem::_RenderAnnotations( HDC hDC, RENDER_DIMENSIONS * pDim, Gdiplus::Rect * pDest, Gdiplus::Rect &src, Gdiplus::Rect &srcAfterClipping )
  723. {
  724. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_RenderAnnotations(%s)"),_szFileName));
  725. if (!_pAnnotations || !hDC)
  726. {
  727. WIA_RETURN_HR(E_INVALIDARG);
  728. }
  729. //
  730. // Save the settings for this DC...
  731. //
  732. INT iSavedDC = SaveDC( hDC );
  733. //
  734. // setup the destination DC:
  735. //
  736. SetMapMode(hDC, MM_TEXT);
  737. SetStretchBltMode(hDC, COLORONCOLOR);
  738. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - dest is (%d,%d) @ (%d,%d)"),_szFileName,pDest->Width,pDest->Height,pDest->X,pDest->Y));
  739. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - rcDevice is (%d,%d) @ (%d,%d)"),_szFileName,pDim->rcDevice.right - pDim->rcDevice.left,pDim->rcDevice.bottom - pDim->rcDevice.top,pDim->rcDevice.left,pDim->rcDevice.top));
  740. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - NominalPhysicalSize is (%d,%d)"),_szFileName,pDim->NominalPhysicalSize.cx,pDim->NominalPhysicalSize.cy));
  741. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - NominalDevicePrintArea is (%d,%d)"),_szFileName,pDim->NominalDevicePrintArea.cx,pDim->NominalDevicePrintArea.cy));
  742. //
  743. // Get device rect
  744. //
  745. Gdiplus::RectF rectDevice;
  746. rectDevice.X = (Gdiplus::REAL)pDim->rcDevice.left;
  747. rectDevice.Y = (Gdiplus::REAL)pDim->rcDevice.top;
  748. rectDevice.Width = (Gdiplus::REAL)(pDim->rcDevice.right - pDim->rcDevice.left);
  749. rectDevice.Height = (Gdiplus::REAL)(pDim->rcDevice.bottom - pDim->rcDevice.top);
  750. //
  751. // Compute LPtoDP scaling factors
  752. //
  753. Gdiplus::REAL xLPtoDP = 0.0;
  754. Gdiplus::REAL yLPtoDP = 0.0;
  755. if (pDim->bDeviceIsScreen)
  756. {
  757. xLPtoDP = rectDevice.Width / (Gdiplus::REAL)pDim->NominalPhysicalSize.cx;
  758. yLPtoDP = rectDevice.Height / (Gdiplus::REAL)pDim->NominalPhysicalSize.cy;
  759. }
  760. else
  761. {
  762. xLPtoDP = rectDevice.Width / (Gdiplus::REAL)pDim->NominalDevicePrintArea.cx;
  763. yLPtoDP = rectDevice.Height / (Gdiplus::REAL)pDim->NominalDevicePrintArea.cy;
  764. }
  765. //
  766. // Get destination rect in device coords...
  767. //
  768. Gdiplus::RectF rectDest;
  769. rectDest.X = pDest->X * xLPtoDP;
  770. rectDest.Y = pDest->Y * xLPtoDP;
  771. rectDest.Width = pDest->Width * xLPtoDP;
  772. rectDest.Height = pDest->Height * xLPtoDP;
  773. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - original source rect is (%d, %d) @ (%d, %d)"),_szFileName,src.Width,src.Height,src.X,src.Y));
  774. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - clipped source rect is (%d, %d) @ (%d, %d)"),_szFileName,srcAfterClipping.Width,srcAfterClipping.Height,srcAfterClipping.X,srcAfterClipping.Y));
  775. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - clipped destination rect in device coords is (%d, %d) @ (%d, %d)"),_szFileName,(INT)rectDest.Width, (INT)rectDest.Height, (INT)rectDest.X, (INT)rectDest.Y));
  776. //
  777. // dx & dy represent how much bigger a destination rectangle would be
  778. // for the whole image, rather than the copped image...
  779. //
  780. Gdiplus::REAL dx = (Gdiplus::REAL)(src.Width - srcAfterClipping.Width) * (rectDest.Width / (Gdiplus::REAL)srcAfterClipping.Width);
  781. Gdiplus::REAL dy = (Gdiplus::REAL)(src.Height - srcAfterClipping.Height) * (rectDest.Height / (Gdiplus::REAL)srcAfterClipping.Height);
  782. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - dx = %d dy = %d"),_szFileName,(INT)dx,(INT)dy));
  783. //
  784. // Set the clipping rectangle on the device hDC in device coords...
  785. //
  786. RECT rcClip;
  787. rcClip.left = (INT)rectDest.X;
  788. rcClip.right = rcClip.left + (INT)rectDest.Width;
  789. rcClip.top = (INT)rectDest.Y;
  790. rcClip.bottom = rcClip.top + (INT)rectDest.Height;
  791. #ifdef SHOW_ANNOT_RECTS
  792. {
  793. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - rcClip is (%d,%d) @ (%d,%d)"),_szFileName,rcClip.right-rcClip.left,rcClip.bottom-rcClip.top,rcClip.left,rcClip.top));
  794. HBRUSH hbr = CreateSolidBrush( RGB( 0xFF, 0x00, 0x00 ) );
  795. FrameRect( hDC, &rcClip, hbr );
  796. DeleteObject( (HGDIOBJ)hbr );
  797. }
  798. #endif
  799. HRGN hrgn = CreateRectRgnIndirect(&rcClip);
  800. if (hrgn != NULL)
  801. {
  802. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - setting clip region to (%d, %d, %d, %d)"),_szFileName,rcClip.left, rcClip.top, rcClip.right, rcClip.bottom));
  803. SelectClipRgn(hDC, hrgn);
  804. }
  805. //
  806. // Make dest rect for whole image, knowing we will clip later...
  807. //
  808. rectDest.X -= (dx / (Gdiplus::REAL)2.0);
  809. rectDest.Y -= (dy / (Gdiplus::REAL)2.0);
  810. rectDest.Width += dx;
  811. rectDest.Height += dy;
  812. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - full dest image rect in device coords is (%d, %d) @ (%d,%d)"),_szFileName,(INT)rectDevice.Width,(INT)rectDevice.Height,(INT)rectDevice.X,(INT)rectDevice.Y));
  813. #ifdef SHOW_ANNOT_RECTS
  814. {
  815. RECT rc;
  816. rc.left = (INT)rectDest.X;
  817. rc.top = (INT)rectDest.Y;
  818. rc.right = rc.left + (INT)rectDest.Width;
  819. rc.bottom = rc.top + (INT)rectDest.Height;
  820. HBRUSH hbr = CreateSolidBrush( RGB( 0x00, 0xFF, 0x00 ) );
  821. FrameRect( hDC, &rc, hbr );
  822. DeleteObject( (HGDIOBJ)hbr );
  823. }
  824. #endif
  825. //
  826. // set up mapping modes for annotations
  827. //
  828. SetMapMode(hDC, MM_ANISOTROPIC);
  829. //
  830. // Set window org/ext to entire image...
  831. //
  832. SetWindowOrgEx(hDC, src.X, src.Y, NULL);
  833. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - set window org to (%d,%d)"),_szFileName,src.X,src.Y));
  834. SetWindowExtEx(hDC, src.Width, src.Height, NULL);
  835. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - set window ext to (%d,%d)"),_szFileName,src.Width,src.Height));
  836. //
  837. // Set the viewport to be at the corner of the image we are trying
  838. // to draw annotations for...
  839. //
  840. SetViewportOrgEx( hDC, (INT)rectDest.X, (INT)rectDest.Y, NULL );
  841. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - set viewport org to (%d,%d)"),_szFileName,(INT)rectDest.X,(INT)rectDest.Y));
  842. //
  843. // We need to set scaling mode of image to dest rect
  844. //
  845. SetViewportExtEx( hDC, (INT)rectDest.Width, (INT)rectDest.Height, NULL );
  846. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - set viewport ext to (%d,%d)"),_szFileName,(INT)rectDest.Width, (INT)rectDest.Height));
  847. //
  848. // Now that everything is set up, render the annotations...
  849. //
  850. WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - calling RenderAllMarks(0x%x)"),_szFileName,hDC));
  851. _pAnnotations->RenderAllMarks(hDC);
  852. SelectClipRgn(hDC, NULL);
  853. if (hrgn != NULL)
  854. DeleteObject(hrgn);
  855. if (iSavedDC)
  856. {
  857. RestoreDC( hDC, iSavedDC );
  858. }
  859. WIA_RETURN_HR(S_OK);
  860. }
  861. /*****************************************************************************
  862. CPhotoItem::_MungeAnnotationDataForThumbnails
  863. If we're rendering using thumbnails, then we need to munge some data
  864. so that we will render correctly...
  865. *****************************************************************************/
  866. HRESULT CPhotoItem::_MungeAnnotationDataForThumbnails( Gdiplus::Rect &src,
  867. Gdiplus::Rect &srcBeforeClipping,
  868. Gdiplus::Rect * pDest,
  869. UINT Flags
  870. )
  871. {
  872. WIA_TRACE((TEXT("CPhotoItem::_MungeAnnotationDataForThumbnails(%s)"),_szFileName));
  873. HRESULT hr = _CreateGdiPlusImage();
  874. if (FAILED(hr))
  875. {
  876. WIA_RETURN_HR(hr);
  877. }
  878. if (!_pImage)
  879. {
  880. WIA_RETURN_HR(E_FAIL);
  881. }
  882. //
  883. // we need to construct the original image rectangle appropriate for
  884. // annotation use...
  885. //
  886. Gdiplus::RectF rectImage;
  887. Gdiplus::REAL scaleY;
  888. hr = _GetImageDimensions( _pImage, rectImage, scaleY );
  889. if (FAILED(hr))
  890. {
  891. //
  892. // If we couldn't accurately get the image dimensions, then bail...
  893. //
  894. return hr;
  895. }
  896. //
  897. // Scale image if it's non-square pixels
  898. //
  899. if (scaleY != (Gdiplus::REAL)0.0)
  900. {
  901. rectImage.Height *= scaleY;
  902. rectImage.Y *= scaleY;
  903. }
  904. WIA_TRACE((TEXT("CPhotoItem::_Munge(%s) - rectImage is (%d,%d) @ (%d,%d)"),_szFileName,(INT)rectImage.Width,(INT)rectImage.Height,(INT)rectImage.X,(INT)rectImage.Y));
  905. //
  906. // Now, do all the transforms on the real image rectangle...
  907. //
  908. const DOUBLE srcAspect = (DOUBLE)rectImage.Width / (DOUBLE)rectImage.Height;
  909. const DOUBLE destAspect = (DOUBLE)pDest->Width / (DOUBLE)pDest->Height;
  910. if((srcAspect >= (DOUBLE)1.0) ^ (destAspect >= (DOUBLE)1.0))
  911. {
  912. //
  913. // Image needs to be rotated, swap width & height
  914. //
  915. rectImage.X = rectImage.Width;
  916. rectImage.Width = rectImage.Height;
  917. rectImage.Height = rectImage.X;
  918. rectImage.X = 0.0;
  919. }
  920. src.X = (INT)rectImage.X;
  921. src.Y = (INT)rectImage.Y;
  922. src.Width = (INT)rectImage.Width;
  923. src.Height = (INT)rectImage.Height;
  924. WIA_TRACE((TEXT("CPhotoItem::_Munge(%s) - srcRect after rotation is (%d,%d) @ (%d,%d)"),_szFileName,src.Width,src.Height,src.X,src.Y));
  925. srcBeforeClipping = src;
  926. if (Flags & RF_CROP_TO_FIT)
  927. {
  928. hr = _CropImage( &src, pDest );
  929. }
  930. else if (Flags & RF_SCALE_TO_FIT)
  931. {
  932. hr = _ScaleImage( &src, pDest );
  933. }
  934. //
  935. // Unscale the src rect
  936. //
  937. if (scaleY != (Gdiplus::REAL)0.0)
  938. {
  939. src.Height = (INT)(((Gdiplus::REAL)src.Height) / scaleY);
  940. src.Y = (INT)(((Gdiplus::REAL)src.Y) / scaleY);
  941. srcBeforeClipping.Height = (INT)(((Gdiplus::REAL)srcBeforeClipping.Height) / scaleY);
  942. srcBeforeClipping.Y = (INT)(((Gdiplus::REAL)srcBeforeClipping.Y) / scaleY);
  943. }
  944. WIA_RETURN_HR(hr);
  945. }
  946. /*****************************************************************************
  947. CPhotoItem::_GetThumbnailQualityImage
  948. Returns in ppImage a pointer to an image class. If pbNeedsToBeDeleted
  949. then the caller must call delete on the returned pImage.
  950. *****************************************************************************/
  951. HRESULT CPhotoItem::_GetThumbnailQualityImage( Gdiplus::Image ** ppImage, RENDER_OPTIONS * pRO, BOOL * pbNeedsToBeDeleted )
  952. {
  953. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_GetThumbnailQualityImage( %s )"),_szFileName));
  954. if (!ppImage || !pRO || !pbNeedsToBeDeleted)
  955. {
  956. WIA_ERROR((TEXT("CPhotoItem::_GetThumbnailQualityImage( %s ) - returning E_INVALIDARG!"),_szFileName));
  957. return E_INVALIDARG;
  958. }
  959. //
  960. // Initialize incoming params
  961. //
  962. *ppImage = NULL;
  963. *pbNeedsToBeDeleted = FALSE;
  964. //
  965. // Make sure we have a GDI+ image class for our thumbnail...
  966. //
  967. SIZE sizeDesired = { DEFAULT_THUMB_WIDTH, DEFAULT_THUMB_HEIGHT };
  968. HRESULT hr = _CreateGdiPlusThumbnail( sizeDesired, pRO->lFrame );
  969. if (SUCCEEDED(hr) && (NULL!=_pThumbnails) && (pRO->lFrame < _lFrameCount) && (NULL!=_pThumbnails[pRO->lFrame]))
  970. {
  971. //
  972. // If we already have thumbnail bits, then use those by creating
  973. // a GDI+ bitmap class over those bits...
  974. //
  975. *ppImage = Gdiplus::Bitmap::FromHBITMAP( _pThumbnails[pRO->lFrame], NULL );
  976. if (*ppImage)
  977. {
  978. hr = Gdiplus2HRESULT((*ppImage)->GetLastStatus());
  979. WIA_TRACE((TEXT("CPhotoItem::_GetThumbnailQualityImage(%s) -- pImage created from thumbnail data is sized as (%d x %d)"),_szFileName,(*ppImage)->GetWidth(),(*ppImage)->GetHeight()));
  980. if (SUCCEEDED(hr))
  981. {
  982. *pbNeedsToBeDeleted = TRUE;
  983. }
  984. else
  985. {
  986. delete (*ppImage);
  987. *ppImage = NULL;
  988. }
  989. }
  990. else
  991. {
  992. hr = E_OUTOFMEMORY;
  993. }
  994. }
  995. else
  996. {
  997. WIA_ERROR((TEXT("CPhotoItem::_GetThumbnailQualityImage(%s) -- no thumbnail exists (_pThumbnails=0x%x, lFrame = %d, _lFrameCount = %d)"),_szFileName,_pThumbnails,pRO->lFrame,_lFrameCount));
  998. }
  999. WIA_RETURN_HR(hr);
  1000. }
  1001. /*****************************************************************************
  1002. CPhotoItem::_GetMediumQualityImage
  1003. Returns in ppImage a pointer to an image class. If pbNeedsToBeDeleted
  1004. then the caller must call delete on the returned pImage.
  1005. *****************************************************************************/
  1006. HRESULT CPhotoItem::_GetMediumQualityImage( Gdiplus::Image ** ppImage, RENDER_OPTIONS * pRO, BOOL * pbNeedsToBeDeleted )
  1007. {
  1008. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_GetMediumQualityImage( %s )"),_szFileName));
  1009. if (!ppImage || !pRO || !pbNeedsToBeDeleted)
  1010. {
  1011. WIA_ERROR((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - returning E_INVALIDARG!"),_szFileName));
  1012. return E_INVALIDARG;
  1013. }
  1014. //
  1015. // Initialize incoming params
  1016. //
  1017. *ppImage = NULL;
  1018. *pbNeedsToBeDeleted = FALSE;
  1019. //
  1020. // We want to use the full high-res image.
  1021. // Make sure we have a GDI+ image class for our photo...
  1022. //
  1023. HRESULT hr = _CreateGdiPlusImage();
  1024. if (SUCCEEDED(hr) && _pImage)
  1025. {
  1026. //
  1027. // If this a metafile type of image, just use the original image
  1028. //
  1029. GUID guidFormat = {0};
  1030. hr = Gdiplus2HRESULT(_pImage->GetRawFormat(&guidFormat));
  1031. if ( (SUCCEEDED(hr) && (guidFormat == ImageFormatIcon)) ||
  1032. (Gdiplus::ImageTypeMetafile == _pImage->GetType())
  1033. )
  1034. {
  1035. WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - this is a metafile or an icon, using the full image..."),_szFileName));
  1036. hr = Gdiplus2HRESULT(_pImage->SelectActiveFrame( _bTimeFrames ? &Gdiplus::FrameDimensionTime : &Gdiplus::FrameDimensionPage, pRO->lFrame ));
  1037. *ppImage = _pImage;
  1038. }
  1039. else
  1040. {
  1041. //
  1042. // Select the specified page
  1043. //
  1044. hr = Gdiplus2HRESULT(_pImage->SelectActiveFrame( _bTimeFrames ? &Gdiplus::FrameDimensionTime : &Gdiplus::FrameDimensionPage, pRO->lFrame ));
  1045. WIA_CHECK_HR(hr,"CPhotoItem::_GetMediumQualityImage() - couldn't select frame!");
  1046. if (SUCCEEDED(hr))
  1047. {
  1048. //
  1049. // Here's the algoritm to decide how big an image to create.
  1050. //
  1051. // (1) At least thumbnail size (120x120)
  1052. // (2) Attempt to scale to either 150dpi or 180dpi, depending on X DPI resolution of the printer
  1053. //
  1054. INT xDPI = 0, yDPI = 0;
  1055. if ((pRO->Dim.DPI.cx % 150) == 0)
  1056. {
  1057. //
  1058. // DPI is some even multiple of 150 (i.e., 150, 300, 600, 1200, 2400, etc)
  1059. //
  1060. xDPI = 150;
  1061. yDPI = MulDiv( pRO->Dim.DPI.cy, xDPI, pRO->Dim.DPI.cx );
  1062. }
  1063. else
  1064. {
  1065. //
  1066. // DPI is some even multiple of 180 (i.e., 180, 360, 720, 1440, 2880, etc)
  1067. //
  1068. xDPI = 180;
  1069. yDPI = MulDiv( pRO->Dim.DPI.cy, xDPI, pRO->Dim.DPI.cx );
  1070. }
  1071. WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - scaling to xDPI=%d yDPI=%d"),_szFileName,xDPI,yDPI));
  1072. //
  1073. // Handle the error case of trying to scale yDPI
  1074. //
  1075. if (yDPI <= 0)
  1076. {
  1077. yDPI = xDPI;
  1078. WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - fixing up yDPI to be %d"),_szFileName,yDPI));
  1079. }
  1080. //
  1081. // Figure out the desired size of the new image...
  1082. //
  1083. INT Width = MulDiv( pRO->pDest->Width, xDPI, 10000 );
  1084. INT Height = MulDiv( pRO->pDest->Height, yDPI, 10000 );
  1085. WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - desired size of image is (%d x %d)"),_szFileName,Width,Height));
  1086. if ((Width < DEFAULT_THUMB_WIDTH) && (Height < DEFAULT_THUMB_HEIGHT))
  1087. {
  1088. Width = 120;
  1089. Height = 120;
  1090. WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - desired size of image is smaller than thumbnail, making it thumbnail size (%d x %d)"),_szFileName,Width,Height));
  1091. }
  1092. //
  1093. // Now we now what size we're trying to scale to, create an image scaled (without cropping) to that size...
  1094. //
  1095. Gdiplus::RectF rectImage;
  1096. Gdiplus::REAL scaleY;
  1097. if (SUCCEEDED(_GetImageDimensions( _pImage, rectImage, scaleY )))
  1098. {
  1099. //
  1100. // scale for non-square pixels...
  1101. //
  1102. if (scaleY != (Gdiplus::REAL)0.0)
  1103. {
  1104. rectImage.Height *= scaleY;
  1105. rectImage.Y *= scaleY;
  1106. }
  1107. SIZE sizeDrawSize = {0};
  1108. sizeDrawSize = PrintScanUtil::ScalePreserveAspectRatio( Width,
  1109. Height,
  1110. (INT)rectImage.Width,
  1111. (INT)rectImage.Height
  1112. );
  1113. WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - size of full image( %d x %d )"),_szFileName, (INT)rectImage.Width, (INT)rectImage.Height));
  1114. WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - sizeDesired( %d x %d )"),_szFileName, Width, Height));
  1115. WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - sizeDrawsize( %d x %d )"),_szFileName, sizeDrawSize.cx, sizeDrawSize.cy));
  1116. //
  1117. // Create the target bitmap and make sure it succeeded
  1118. //
  1119. *ppImage = (Gdiplus::Image *)new Gdiplus::Bitmap( sizeDrawSize.cx, sizeDrawSize.cy );
  1120. if (*ppImage)
  1121. {
  1122. hr = Gdiplus2HRESULT((*ppImage)->GetLastStatus());
  1123. if (SUCCEEDED(hr))
  1124. {
  1125. //
  1126. // Set the resolution (DPI) for the bitmap
  1127. //
  1128. ((Gdiplus::Bitmap *)(*ppImage))->SetResolution( (Gdiplus::REAL)xDPI, (Gdiplus::REAL)yDPI );
  1129. //
  1130. // Get a graphics to render to
  1131. //
  1132. Graphics *pGraphics = Gdiplus::Graphics::FromImage(*ppImage);
  1133. if (pGraphics)
  1134. {
  1135. hr = Gdiplus2HRESULT(pGraphics->GetLastStatus());
  1136. //
  1137. // Make sure it is valid
  1138. //
  1139. if (SUCCEEDED(hr))
  1140. {
  1141. //
  1142. // Set the interpolation mode to high quality
  1143. //
  1144. pGraphics->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBicubic );
  1145. //
  1146. // Set the smoothing (anti-aliasing) mode to high quality as well
  1147. //
  1148. pGraphics->SetSmoothingMode( Gdiplus::SmoothingModeHighQuality );
  1149. //
  1150. // Draw scaled image
  1151. //
  1152. WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - calling pGraphics->DrawImage( _pImage, 0, 0, %d, %d )"),_szFileName,sizeDrawSize.cx,sizeDrawSize.cy));
  1153. Gdiplus::Rect rectDest;
  1154. rectDest.X = 0;
  1155. rectDest.Y = 0;
  1156. rectDest.Width = sizeDrawSize.cx;
  1157. rectDest.Height = sizeDrawSize.cy;
  1158. Gdiplus::ImageAttributes imageAttr;
  1159. imageAttr.SetWrapMode( Gdiplus::WrapModeTileFlipXY, Gdiplus::Color(), FALSE );
  1160. //
  1161. // Undo scaling
  1162. //
  1163. if (scaleY != (Gdiplus::REAL)0.0)
  1164. {
  1165. rectImage.Height /= scaleY;
  1166. rectImage.Y /= scaleY;
  1167. }
  1168. //
  1169. // Finally render the image w/the right settings
  1170. //
  1171. pGraphics->DrawImage( _pImage, rectDest, 0, 0, (INT)rectImage.Width, (INT)rectImage.Height, Gdiplus::UnitPixel, &imageAttr );
  1172. WIA_CHECK_HR(hr,"CPhotoItem::_GetMediumQualityImage() - pGraphics->DrawImage( _pImage, 0, 0, sizeDrawSize.cx, sizeDrawSize.cy ) failed!");
  1173. if (SUCCEEDED(hr))
  1174. {
  1175. *pbNeedsToBeDeleted = TRUE;
  1176. }
  1177. else
  1178. {
  1179. delete (*ppImage);
  1180. *ppImage = NULL;
  1181. }
  1182. }
  1183. //
  1184. // Clean up our dynamically allocated graphics
  1185. //
  1186. delete pGraphics;
  1187. }
  1188. else
  1189. {
  1190. WIA_ERROR((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - pGraphics was NULL!"),_szFileName));
  1191. hr = E_OUTOFMEMORY;
  1192. }
  1193. }
  1194. else
  1195. {
  1196. delete (*ppImage);
  1197. *ppImage = NULL;
  1198. }
  1199. }
  1200. else
  1201. {
  1202. WIA_ERROR((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - failed to create new pImage for medium quality data!"),_szFileName));
  1203. hr = E_OUTOFMEMORY;
  1204. }
  1205. }
  1206. }
  1207. }
  1208. }
  1209. WIA_RETURN_HR(hr);
  1210. }
  1211. /*****************************************************************************
  1212. CPhotoItem::_GetFullQualityImage
  1213. Returns in ppImage a pointer to an image class. If pbNeedsToBeDeleted
  1214. then the caller must call delete on the returned pImage.
  1215. *****************************************************************************/
  1216. HRESULT CPhotoItem::_GetFullQualityImage( Gdiplus::Image ** ppImage, RENDER_OPTIONS * pRO, BOOL * pbNeedsToBeDeleted )
  1217. {
  1218. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_GetFullQualityImage( %s )"),_szFileName));
  1219. if (!ppImage || !pbNeedsToBeDeleted)
  1220. {
  1221. WIA_ERROR((TEXT("CPhotoItem::_GetFullQualityImage(%s) - returning E_INVALIDARG!"),_szFileName));
  1222. return E_INVALIDARG;
  1223. }
  1224. //
  1225. // Initialize incoming params
  1226. //
  1227. *ppImage = NULL;
  1228. *pbNeedsToBeDeleted = FALSE;
  1229. //
  1230. // We want to use the full high-res image.
  1231. // Make sure we have a GDI+ image class for our photo...
  1232. //
  1233. HRESULT hr = _CreateGdiPlusImage();
  1234. if (SUCCEEDED(hr) && _pImage)
  1235. {
  1236. //
  1237. // Select the specified page
  1238. //
  1239. hr = Gdiplus2HRESULT(_pImage->SelectActiveFrame( _bTimeFrames ? &Gdiplus::FrameDimensionTime : &Gdiplus::FrameDimensionPage, pRO->lFrame ));
  1240. if (SUCCEEDED(hr))
  1241. {
  1242. *ppImage = _pImage;
  1243. WIA_TRACE((TEXT("CPhotoItem::_GetFullQualityImage(%s) -- *ppImage created from full image data is sized as (%d x %d)"),_szFileName,_pImage->GetWidth(),_pImage->GetHeight()));
  1244. }
  1245. else
  1246. {
  1247. WIA_ERROR((TEXT("CPhotoItem::_GetFullQualityImage(%s) - couldn't select frame %d, hr = 0x%x"),_szFileName,pRO->lFrame,hr));
  1248. }
  1249. }
  1250. WIA_RETURN_HR(hr);
  1251. }
  1252. #define CHECK_AND_EXIT_ON_FAILURE(hr) if (FAILED(hr)) {if (pImage && (pImage!=_pImage)) {delete pImage;} WIA_RETURN_HR(hr);}
  1253. /*****************************************************************************
  1254. CPhotoItem::Render
  1255. Renders the given item into the Graphics that is supplied...
  1256. *****************************************************************************/
  1257. HRESULT CPhotoItem::Render( RENDER_OPTIONS * pRO )
  1258. {
  1259. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::Render( %s, pRO = 0x%x)"),_szFileName,pRO));
  1260. if (!pRO)
  1261. {
  1262. WIA_ERROR((TEXT("CPhotoItem::Render(%s) - pRO is NULL, don't have any input!"),_szFileName));
  1263. return E_INVALIDARG;
  1264. }
  1265. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - Render Options were specificed as:"),_szFileName));
  1266. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - g = 0x%x"),_szFileName,pRO->g));
  1267. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - pDest = (%d x %d) at (%d,%d)"),_szFileName,pRO->pDest->Width,pRO->pDest->Height,pRO->pDest->X,pRO->pDest->Y));
  1268. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - Flags = 0x%x"),_szFileName,pRO->Flags));
  1269. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - lFrame = %d"),_szFileName,pRO->lFrame));
  1270. HRESULT hr = S_OK;
  1271. Gdiplus::Image * pImage = NULL;
  1272. BOOL bNeedsToBeDeleted = FALSE;
  1273. Gdiplus::GpStatus status;
  1274. //
  1275. // Check for bad args...
  1276. //
  1277. if (!pRO->g)
  1278. {
  1279. WIA_ERROR((TEXT("CPhotoItem::Render(%s) - g is NULL, can't draw anything"),_szFileName));
  1280. return E_INVALIDARG;
  1281. }
  1282. if ((pRO->Flags & RF_STRETCH_TO_FIT) && (pRO->Flags & (RF_CROP_TO_FIT | RF_SCALE_TO_FIT)))
  1283. {
  1284. WIA_ERROR((TEXT("CPhotoItem::Render(%s) - RF_STRETCH_TO_FIT can't be combined with CROP or SCALE"),_szFileName));
  1285. return E_INVALIDARG;
  1286. }
  1287. CAutoCriticalSection lock(_csItem);
  1288. //
  1289. // Refresh annotation data if we have it
  1290. //
  1291. _LoadAnnotations();
  1292. if (pRO->Flags & RF_USE_THUMBNAIL_DATA)
  1293. {
  1294. WIA_TRACE((TEXT("CPhotoItem::Render(%s) -- render using thumbnail data..."),_szFileName));
  1295. hr = _GetThumbnailQualityImage( &pImage, pRO, &bNeedsToBeDeleted );
  1296. }
  1297. else if (pRO->Flags & RF_USE_MEDIUM_QUALITY_DATA)
  1298. {
  1299. WIA_TRACE((TEXT("CPhotoItem::Render(%s) -- render using high quality thumbnail data..."),_szFileName));
  1300. hr = _GetMediumQualityImage( &pImage, pRO, &bNeedsToBeDeleted );
  1301. }
  1302. else if (pRO->Flags & RF_USE_FULL_IMAGE_DATA)
  1303. {
  1304. WIA_TRACE((TEXT("CPhotoItem::Render(%s) -- render using full image data..."),_szFileName));
  1305. hr = _GetFullQualityImage( &pImage, pRO, &bNeedsToBeDeleted );
  1306. }
  1307. else
  1308. {
  1309. WIA_ERROR((TEXT("CPhotoItem::Render(%s) -- bad render data flags"),_szFileName));
  1310. WIA_RETURN_HR(E_INVALIDARG);
  1311. }
  1312. CHECK_AND_EXIT_ON_FAILURE(hr);
  1313. //
  1314. // We've constructed the appropriate image, now try to load the annotations...
  1315. //
  1316. if (_pAnnotBits && _pAnnotBits[pRO->lFrame] && _pAnnotations && _pImage)
  1317. {
  1318. _pAnnotations->BuildAllMarksFromData( _pAnnotBits[pRO->lFrame]->value,
  1319. _pAnnotBits[pRO->lFrame]->length,
  1320. (ULONG)_pImage->GetHorizontalResolution(),
  1321. (ULONG)_pImage->GetVerticalResolution()
  1322. );
  1323. WIA_TRACE((TEXT("CPhotoItem::Render(%s) -- %d annotation marks for frame %d found and initialized"),_szFileName,_pAnnotations->GetCount(),pRO->lFrame));
  1324. }
  1325. //
  1326. // Get the dimensions of the source image...
  1327. //
  1328. Gdiplus::Rect src;
  1329. //
  1330. // Do this so EMF/WMF print and draw correctly...
  1331. //
  1332. Gdiplus::RectF rectBounds;
  1333. Gdiplus::REAL scaleY;
  1334. hr = _GetImageDimensions( pImage, rectBounds, scaleY );
  1335. if (SUCCEEDED(hr))
  1336. {
  1337. src.Width = (INT)rectBounds.Width;
  1338. src.Height = (INT)(rectBounds.Height * scaleY);
  1339. src.X = (INT)rectBounds.X;
  1340. src.Y = (INT)(rectBounds.Y * scaleY);
  1341. }
  1342. CHECK_AND_EXIT_ON_FAILURE(hr);
  1343. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - srcRect is (%d,%d) @ (%d,%d) before any changes"),_szFileName,src.Width, src.Height, src.X, src.Y));
  1344. //
  1345. // do any needed rotation
  1346. //
  1347. hr = _DoHandleRotation( pImage, src, pRO->pDest, pRO->Flags, scaleY );
  1348. CHECK_AND_EXIT_ON_FAILURE(hr);
  1349. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - srcRect is (%d,%d) @ (%d,%d) after any needed rotation"),_szFileName,src.Width,src.Height,src.X,src.Y));
  1350. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - destRect is (%d,%d) @ (%d,%d) after any needed rotation"),_szFileName,pRO->pDest->Width,pRO->pDest->Height,pRO->pDest->X,pRO->pDest->Y));
  1351. //
  1352. // If things are still good, do croping/scaling and then draw the image...
  1353. //
  1354. // First check if we should crop...
  1355. //
  1356. Gdiplus::Rect srcBeforeClipping = src;
  1357. if (pRO->Flags & (RF_CROP_TO_FIT | RF_SCALE_TO_FIT))
  1358. {
  1359. #ifdef DEBUG
  1360. if (pRO->Flags & RF_CROP_TO_FIT)
  1361. {
  1362. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - RF_CROP_TO_FIT was specified"),_szFileName));
  1363. }
  1364. if (pRO->Flags & RF_SCALE_TO_FIT)
  1365. {
  1366. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - RF_SCALE_TO_FIT was specified"),_szFileName));
  1367. }
  1368. #endif
  1369. if (pRO->Flags & RF_CROP_TO_FIT)
  1370. {
  1371. hr = _CropImage( &src, pRO->pDest );
  1372. }
  1373. else if (pRO->Flags & RF_SCALE_TO_FIT)
  1374. {
  1375. hr = _ScaleImage( &src, pRO->pDest );
  1376. }
  1377. else
  1378. {
  1379. WIA_ERROR((TEXT("CPhotoItem::Render(%s) - CropScale: unknown configuration"),_szFileName));
  1380. hr = E_FAIL;
  1381. }
  1382. }
  1383. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - srcRect is (%d,%d) @ (%d,%d) after scaling"),_szFileName,src.Width, src.Height, src.X, src.Y));
  1384. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - destRect is (%d,%d) @ (%d,%d) after scaling"),_szFileName,pRO->pDest->Width, pRO->pDest->Height, pRO->pDest->X, pRO->pDest->Y));
  1385. CHECK_AND_EXIT_ON_FAILURE(hr);
  1386. //
  1387. // set the destination rectangle...
  1388. //
  1389. Gdiplus::Rect destTemp( pRO->pDest->X, pRO->pDest->Y, pRO->pDest->Width, pRO->pDest->Height );
  1390. //
  1391. // If this is a non-square pixel image, we need to reset the source rectangle to be back to actual
  1392. // pixels, instead of incorporating DPI as well...
  1393. //
  1394. if ((scaleY != (Gdiplus::REAL)0.0) && (scaleY != (Gdiplus::REAL)1.0))
  1395. {
  1396. src.Height = (INT)((Gdiplus::REAL)src.Height / scaleY);
  1397. src.Y = (INT)((Gdiplus::REAL)src.Y / scaleY);
  1398. srcBeforeClipping.Height = (INT)((Gdiplus::REAL)srcBeforeClipping.Height / scaleY);
  1399. srcBeforeClipping.Y = (INT)((Gdiplus::REAL)srcBeforeClipping.Y / scaleY);
  1400. }
  1401. //
  1402. // Set the interpolation mode to high quality
  1403. //
  1404. pRO->g->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBicubic );
  1405. //
  1406. // Set the smoothing (anti-aliasing) mode to high quality as well
  1407. //
  1408. pRO->g->SetSmoothingMode( Gdiplus::SmoothingModeHighQuality );
  1409. //
  1410. // Set the wrap mode
  1411. //
  1412. Gdiplus::ImageAttributes imageAttr;
  1413. imageAttr.SetWrapMode( Gdiplus::WrapModeTileFlipXY, Gdiplus::Color(), FALSE );
  1414. //
  1415. // Time to draw the image.
  1416. //
  1417. WIA_TRACE((TEXT("CPhotoItem::Render(%s) - calling DrawImage( pImage, destTemp( %d x %d at %d,%d ), %d, %d, %d, %d )"),_szFileName,destTemp.Width,destTemp.Height,destTemp.X,destTemp.Y,src.X,src.Y,src.Width,src.Height));
  1418. status = pRO->g->DrawImage( pImage, destTemp, src.X, src.Y, src.Width, src.Height, Gdiplus::UnitPixel, &imageAttr );
  1419. //
  1420. // Check for errors, and then draw annotations...
  1421. //
  1422. hr = Gdiplus2HRESULT(status);
  1423. WIA_CHECK_HR(hr,"CPhotoItem::Render() - g->DrawImage( pImage ) failed!");
  1424. //
  1425. // Render annotations if they exist...
  1426. //
  1427. if (_pAnnotations && _pAnnotBits && _pAnnotBits[pRO->lFrame])
  1428. {
  1429. if ((pRO->Flags & RF_USE_THUMBNAIL_DATA) || (pRO->Flags & RF_USE_MEDIUM_QUALITY_DATA))
  1430. {
  1431. _MungeAnnotationDataForThumbnails( src, srcBeforeClipping, pRO->pDest, pRO->Flags );
  1432. }
  1433. HDC hdcTemp = pRO->g->GetHDC();
  1434. if ((Gdiplus::Ok == pRO->g->GetLastStatus()) && hdcTemp)
  1435. {
  1436. _RenderAnnotations( hdcTemp, &pRO->Dim, pRO->pDest, srcBeforeClipping, src );
  1437. pRO->g->ReleaseHDC( hdcTemp );
  1438. }
  1439. }
  1440. //
  1441. // If we created a new object for the image bits then delete it here...
  1442. //
  1443. if (bNeedsToBeDeleted)
  1444. {
  1445. delete pImage;
  1446. }
  1447. //
  1448. // To save memory, once we have rendered the full image we discard it
  1449. // so the memory can be reclaimed...
  1450. //
  1451. if ((pRO->Flags & RF_USE_FULL_IMAGE_DATA) || (pRO->Flags & RF_USE_MEDIUM_QUALITY_DATA))
  1452. {
  1453. _DiscardGdiPlusImages();
  1454. }
  1455. WIA_RETURN_HR(hr);
  1456. }
  1457. /*****************************************************************************
  1458. CPhotoItem::_LoadAnnotations
  1459. If there are annotations in this image, load them...
  1460. *****************************************************************************/
  1461. HRESULT CPhotoItem::_LoadAnnotations()
  1462. {
  1463. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_LoadAnnotations(%s)"),_szFileName));
  1464. //
  1465. // Only do this if we don't already have the data and haven't already
  1466. // tried to load this before and found out there are no annotations...
  1467. //
  1468. if (!_pAnnotBits && !_pAnnotations && !_bWeKnowAnnotationsDontExist)
  1469. {
  1470. //
  1471. // Ensure we have the image...
  1472. //
  1473. _CreateGdiPlusImage();
  1474. //
  1475. // Make sure we have frame data
  1476. //
  1477. LONG lDummy = 0;
  1478. GetImageFrameCount( &lDummy );
  1479. //
  1480. // If we have any annotations, then load them up accross all the frames...
  1481. //
  1482. UINT uSize = 0;
  1483. BOOL bHasAnnotations = FALSE;
  1484. Gdiplus::Status status;
  1485. _pAnnotations = (CAnnotationSet *)new CAnnotationSet();
  1486. if (_pAnnotations)
  1487. {
  1488. _pAnnotBits = (Gdiplus::PropertyItem **) new BYTE[ sizeof(LPVOID) * _lFrameCount ];
  1489. if (_pAnnotBits)
  1490. {
  1491. for (LONG lCurFrame=0; lCurFrame < _lFrameCount; lCurFrame++)
  1492. {
  1493. status = _pImage->SelectActiveFrame( _bTimeFrames ? &Gdiplus::FrameDimensionTime : &Gdiplus::FrameDimensionPage, lCurFrame );
  1494. if (Gdiplus::Ok == status)
  1495. {
  1496. //
  1497. // Load the annotation bits for this frame...
  1498. //
  1499. uSize = _pImage->GetPropertyItemSize( ANNOTATION_IMAGE_TAG );
  1500. if (uSize > 0)
  1501. {
  1502. _pAnnotBits[lCurFrame] = (Gdiplus::PropertyItem *) new BYTE[ uSize ];
  1503. if (_pAnnotBits[lCurFrame])
  1504. {
  1505. //
  1506. // Read the annotations tag from the file...
  1507. //
  1508. status = _pImage->GetPropertyItem( ANNOTATION_IMAGE_TAG, uSize, _pAnnotBits[lCurFrame] );
  1509. if ((Gdiplus::Ok == status) && _pAnnotBits[lCurFrame])
  1510. {
  1511. bHasAnnotations = TRUE;
  1512. }
  1513. else
  1514. {
  1515. WIA_ERROR((TEXT("CPhotoItem::_LoadAnnotations - GetPropertyItem failed w/hr=0x%x"),Gdiplus2HRESULT(status)));
  1516. }
  1517. }
  1518. else
  1519. {
  1520. WIA_ERROR((TEXT("CPhotoItem::_LoadAnnotations - couldn't create _pAnnotBits[%d]"),lCurFrame));
  1521. }
  1522. }
  1523. else
  1524. {
  1525. WIA_TRACE((TEXT("CPhotoItem::_LoadAnnotations - GetPropertyItemSize returned %d size"),uSize));
  1526. }
  1527. }
  1528. else
  1529. {
  1530. WIA_ERROR((TEXT("CPhotoItem::_LoadAnnotations - SelectActiveFrame(%d) failed w/hr=0x%x"),lCurFrame,Gdiplus2HRESULT(status)));
  1531. }
  1532. }
  1533. }
  1534. else
  1535. {
  1536. WIA_ERROR((TEXT("CPhotoItem::_LoadAnnotations - couldn't create _pAnnotBits")));
  1537. delete _pAnnotations;
  1538. _pAnnotations = NULL;
  1539. }
  1540. }
  1541. else
  1542. {
  1543. WIA_ERROR((TEXT("CPhotoItem::_LoadAnnotations - couldn't create _pAnnotations!")));
  1544. }
  1545. if (!bHasAnnotations)
  1546. {
  1547. WIA_TRACE((TEXT("CPhotoItem::_LoadAnnotations - no annotations were found!")));
  1548. //
  1549. // delete anything we created, as we didn't load any annotations...
  1550. //
  1551. if (_pAnnotBits)
  1552. {
  1553. for (LONG l=0; l < _lFrameCount; l++)
  1554. {
  1555. delete [] _pAnnotBits[l];
  1556. _pAnnotBits[l] = NULL;
  1557. }
  1558. delete [] _pAnnotBits;
  1559. _pAnnotBits = NULL;
  1560. }
  1561. if (_pAnnotations)
  1562. {
  1563. delete _pAnnotations;
  1564. _pAnnotations = NULL;
  1565. }
  1566. //
  1567. // We gave it our best shot -- there aren't any annotations
  1568. // so don't bother trying again for this session of the wizard
  1569. // for this image...
  1570. _bWeKnowAnnotationsDontExist = TRUE;
  1571. }
  1572. }
  1573. else
  1574. {
  1575. WIA_TRACE((TEXT("CPhotoItem::_LoadAnnotations - not loading because we already have pointers to the data.")));
  1576. }
  1577. WIA_RETURN_HR(S_OK);
  1578. }
  1579. /*****************************************************************************
  1580. CPhotoItem::_CreateGdiPlusImage
  1581. Instantiates Gdi+ plus over the given image...
  1582. *****************************************************************************/
  1583. HRESULT CPhotoItem::_CreateGdiPlusImage()
  1584. {
  1585. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_CreateGdiPlusImage(%s)"),_szFileName));
  1586. HRESULT hr = S_OK;
  1587. CAutoCriticalSection lock(_csItem);
  1588. //
  1589. // Try and get the size of the file...
  1590. //
  1591. if (_llFileSize == 0)
  1592. {
  1593. TCHAR szPath[ MAX_PATH + 64 ];
  1594. *szPath = 0;
  1595. if (SHGetPathFromIDList( _pidlFull, szPath ) && *szPath)
  1596. {
  1597. HANDLE hFile = CreateFile( szPath,
  1598. GENERIC_READ,
  1599. FILE_SHARE_READ,
  1600. NULL,
  1601. OPEN_EXISTING,
  1602. FILE_ATTRIBUTE_NORMAL,
  1603. NULL
  1604. );
  1605. if (hFile != INVALID_HANDLE_VALUE)
  1606. {
  1607. LARGE_INTEGER li;
  1608. if (GetFileSizeEx( hFile, &li ))
  1609. {
  1610. _llFileSize = li.QuadPart;
  1611. }
  1612. CloseHandle( hFile );
  1613. }
  1614. }
  1615. }
  1616. //
  1617. // Make sure we've got a stream pointer to the file
  1618. //
  1619. if (!_pImage)
  1620. {
  1621. //
  1622. // Get an IStream pointer for our item
  1623. //
  1624. CComPtr<IShellFolder> psfDesktop;
  1625. hr = SHGetDesktopFolder( &psfDesktop );
  1626. if (SUCCEEDED(hr) && psfDesktop)
  1627. {
  1628. hr = psfDesktop->BindToObject( _pidlFull, NULL, IID_IStream, (LPVOID *)&_pStream );
  1629. WIA_CHECK_HR(hr,"_CreateGdiPlusImage: psfDesktop->BindToObject( IStream for _pidlFull )");
  1630. if (SUCCEEDED(hr) && _pStream)
  1631. {
  1632. //
  1633. // Create GDI+ image object from stream
  1634. //
  1635. _pImage = new Gdiplus::Image( _pStream, TRUE );
  1636. if (!_pImage)
  1637. {
  1638. _pStream = NULL;
  1639. WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusImage(%s) - _pImage is NULL, creation of GDI+ image object failed!"),_szFileName));
  1640. hr = E_OUTOFMEMORY;
  1641. }
  1642. else
  1643. {
  1644. hr = Gdiplus2HRESULT(_pImage->GetLastStatus());
  1645. if (FAILED(hr))
  1646. {
  1647. delete _pImage;
  1648. _pImage = NULL;
  1649. _pStream = NULL;
  1650. WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusImage(%s) - creation of image failed w/GDI+ hr = 0x%x"),_szFileName,hr));
  1651. }
  1652. }
  1653. }
  1654. }
  1655. else
  1656. {
  1657. WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusImage(%s) - Couldn't get psfDesktop!"),_szFileName));
  1658. }
  1659. }
  1660. WIA_RETURN_HR(hr);
  1661. }
  1662. /*****************************************************************************
  1663. CPhotoItem::_CreateGdiPlusThumbnail
  1664. Ensure we have a GdiPlus::Image for the thumbnail
  1665. *****************************************************************************/
  1666. HRESULT CPhotoItem::_CreateGdiPlusThumbnail( const SIZE &sizeDesired, LONG lFrame )
  1667. {
  1668. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_CreateGdiPlusThumbnail( %s, this = 0x%x )"),_szFileName,this));
  1669. HRESULT hr = S_OK;
  1670. Gdiplus::GpStatus status = Gdiplus::Ok;
  1671. CAutoCriticalSection lock(_csItem);
  1672. //
  1673. // Ensure we have backing Image for file..
  1674. //
  1675. hr = _CreateGdiPlusImage();
  1676. if (SUCCEEDED(hr) && _pImage)
  1677. {
  1678. //
  1679. // Get the number of frames...
  1680. //
  1681. LONG lFrameCount = 0;
  1682. hr = GetImageFrameCount( &lFrameCount );
  1683. if (SUCCEEDED(hr))
  1684. {
  1685. //
  1686. // Our primary goal is to get at the thumbnail bitmap bits for the
  1687. // specified frame. First, make sure we've got an array to place
  1688. // these in to.
  1689. //
  1690. if ((!_pThumbnails) && (lFrameCount >= 1))
  1691. {
  1692. WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - _pThumbnails(0x%x) and _lFrameCount(%d)"),_szFileName,_pThumbnails,lFrameCount));
  1693. _pThumbnails = (HBITMAP *) new HBITMAP [lFrameCount];
  1694. if (_pThumbnails)
  1695. {
  1696. //
  1697. // Ensure we start out with NULL HBITMAPS...
  1698. //
  1699. for (INT i=0; i<lFrameCount; i++)
  1700. {
  1701. _pThumbnails[i] = NULL;
  1702. }
  1703. }
  1704. else
  1705. {
  1706. hr = E_OUTOFMEMORY;
  1707. }
  1708. WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - _pThumbnails is now (0x%x)"),_szFileName,_pThumbnails));
  1709. }
  1710. if (SUCCEEDED(hr) && _pThumbnails)
  1711. {
  1712. WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - we have _pThumbnails"),_szFileName));
  1713. //
  1714. // Do we already have thumbnail bits for this frame?
  1715. //
  1716. if ((lFrame < lFrameCount) && (!_pThumbnails[lFrame]))
  1717. {
  1718. //
  1719. // Have to create thumbnail for this frame.
  1720. // Select the specified frame.
  1721. //
  1722. status = _pImage->SelectActiveFrame( _bTimeFrames ? &Gdiplus::FrameDimensionTime : &Gdiplus::FrameDimensionPage, lFrame );
  1723. hr = Gdiplus2HRESULT(status);
  1724. if (SUCCEEDED(hr))
  1725. {
  1726. GUID guidFormat = {0};
  1727. Gdiplus::Image * pThumb = NULL;
  1728. status = _pImage->GetRawFormat( &guidFormat );
  1729. if (status == Gdiplus::Ok && (guidFormat == ImageFormatIcon))
  1730. {
  1731. pThumb = _pImage;
  1732. }
  1733. else
  1734. {
  1735. pThumb = _pImage->GetThumbnailImage( 0, 0, NULL, NULL );
  1736. }
  1737. if (pThumb)
  1738. {
  1739. WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - SIZE - _pImage (%d x %d )"),_szFileName,_pImage->GetWidth(), _pImage->GetHeight()));
  1740. WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - SIZE - pThumb (%d x %d)"),_szFileName,pThumb->GetWidth(),pThumb->GetHeight()));
  1741. //
  1742. // Scale image to fill thumbnail space while preserving
  1743. // aspect ratio...
  1744. //
  1745. SIZE sizeDrawSize = {0};
  1746. sizeDrawSize = PrintScanUtil::ScalePreserveAspectRatio( sizeDesired.cx,
  1747. sizeDesired.cy,
  1748. _pImage->GetWidth(),
  1749. _pImage->GetHeight()
  1750. );
  1751. WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - SIZE - sizeDesired( %d x %d )"),_szFileName,sizeDesired.cx, sizeDesired.cy));
  1752. WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - SIZE - sizeDrawsize( %d x %d )"),_szFileName,sizeDrawSize.cx, sizeDrawSize.cy));
  1753. //
  1754. // Create an HBITMAP of the thumbnail...
  1755. //
  1756. Gdiplus::Bitmap * pBitmap = new Gdiplus::Bitmap( sizeDrawSize.cx, sizeDrawSize.cy );
  1757. if (pBitmap)
  1758. {
  1759. hr = Gdiplus2HRESULT(pBitmap->GetLastStatus());
  1760. if (SUCCEEDED(hr))
  1761. {
  1762. //
  1763. // Get a graphics to render to
  1764. //
  1765. Graphics *pGraphics = Gdiplus::Graphics::FromImage((Gdiplus::Image *)pBitmap);
  1766. if (pGraphics)
  1767. {
  1768. hr = Gdiplus2HRESULT(pGraphics->GetLastStatus());
  1769. //
  1770. // Make sure it is valid
  1771. //
  1772. if (SUCCEEDED(hr))
  1773. {
  1774. //
  1775. // erase the background of the image
  1776. //
  1777. pGraphics->Clear( g_wndColor );
  1778. //
  1779. // Set the interpolation mode to high quality
  1780. //
  1781. pGraphics->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBilinear );
  1782. //
  1783. // Draw scaled image
  1784. //
  1785. WIA_TRACE((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - calling pGraphics->DrawImage( pThumb, %d, %d, %d, %d )"),_szFileName,0 + ((sizeDesired.cx - sizeDrawSize.cx) / 2),0 + ((sizeDesired.cy - sizeDrawSize.cy) / 2),sizeDrawSize.cx,sizeDrawSize.cy));
  1786. hr = Gdiplus2HRESULT(pGraphics->DrawImage( pThumb,
  1787. 0,
  1788. 0,
  1789. sizeDrawSize.cx,
  1790. sizeDrawSize.cy
  1791. ));
  1792. WIA_CHECK_HR(hr,"CPhotoItem::GetThumbnailBitmap() - pGraphics->DrawImage( pThumb ) failed!");
  1793. if (SUCCEEDED(hr))
  1794. {
  1795. DWORD dw = GetSysColor( COLOR_WINDOW );
  1796. Gdiplus::Color wndClr(255,GetRValue(dw),GetGValue(dw),GetBValue(dw));
  1797. pBitmap->GetHBITMAP( wndClr, &_pThumbnails[lFrame] );
  1798. }
  1799. }
  1800. //
  1801. // Clean up our dynamically allocated graphics
  1802. //
  1803. delete pGraphics;
  1804. }
  1805. else
  1806. {
  1807. WIA_ERROR((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - pGraphics was NULL!"),_szFileName));
  1808. hr = E_OUTOFMEMORY;
  1809. }
  1810. }
  1811. else
  1812. {
  1813. WIA_ERROR((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - pImage failed to be created, hr = 0x%x"),hr));
  1814. }
  1815. delete pBitmap;
  1816. }
  1817. if (pThumb != _pImage)
  1818. {
  1819. delete pThumb;
  1820. }
  1821. }
  1822. else
  1823. {
  1824. hr = Gdiplus2HRESULT(_pImage->GetLastStatus());
  1825. WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s): unable to GetThumbnailImage"),_szFileName));
  1826. }
  1827. }
  1828. else
  1829. {
  1830. WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s): unable to select frame %d"),_szFileName,lFrame));
  1831. }
  1832. }
  1833. else
  1834. {
  1835. WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s): we already have _pThumbnails[%d], it is (0x%x)"),_szFileName,lFrame,_pThumbnails[lFrame]));
  1836. }
  1837. }
  1838. }
  1839. else
  1840. {
  1841. WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s): _pImage was NULL!"),_szFileName));
  1842. }
  1843. }
  1844. else
  1845. {
  1846. WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s): _pImage was NULL!"),_szFileName));
  1847. }
  1848. WIA_RETURN_HR(hr);
  1849. }
  1850. /*****************************************************************************
  1851. CPhotoItem::_DiscardGdiPlusImages
  1852. Releases GDI+ objects for thumbnail and image
  1853. *****************************************************************************/
  1854. HRESULT CPhotoItem::_DiscardGdiPlusImages()
  1855. {
  1856. WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_DiscardGdiPlusImages(%s)"),_szFileName));
  1857. CAutoCriticalSection lock(_csItem);
  1858. //
  1859. // Clear out GDI+ image objects...
  1860. //
  1861. if (_pImage)
  1862. {
  1863. delete _pImage;
  1864. _pImage = NULL;
  1865. }
  1866. if (_pStream)
  1867. {
  1868. //
  1869. // Since this is an CComPtr, setting this to NULL will free
  1870. // the reference on the stream object...
  1871. //
  1872. _pStream = NULL;
  1873. }
  1874. if (_pClassBitmap)
  1875. {
  1876. delete _pClassBitmap;
  1877. _pClassBitmap = NULL;
  1878. }
  1879. if (_pAnnotations)
  1880. {
  1881. delete _pAnnotations;
  1882. _pAnnotations = NULL;
  1883. }
  1884. if (_pAnnotBits)
  1885. {
  1886. for (INT i=0; i < _lFrameCount; i++)
  1887. {
  1888. delete [] _pAnnotBits[i];
  1889. _pAnnotBits[i] = NULL;
  1890. }
  1891. delete [] _pAnnotBits;
  1892. _pAnnotBits = NULL;
  1893. }
  1894. return S_OK;
  1895. }