/***************************************************************************** * * (C) COPYRIGHT MICROSOFT CORPORATION, 2000 * * TITLE: item.cpp * * VERSION: 1.0 * * AUTHOR: RickTu * * DATE: 10/18/00 * * DESCRIPTION: Implements an item class that encapsulates the photos * we are dealing with. * *****************************************************************************/ #include #pragma hdrstop /***************************************************************************** _ScaleImage Scales src rect to fit into dest rect while preserving aspect ratio *****************************************************************************/ HRESULT _ScaleImage( Gdiplus::Rect * pSrc, Gdiplus::Rect * pDest ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("_ScaleImage()"))); if (!pDest || !pSrc) { WIA_ERROR((TEXT("_ScaleImage: bad params, exiting early!"))); return E_INVALIDARG; } WIA_TRACE((TEXT("_ScaleImage: src before scaling: (%d, %d) @ (%d, %d)"), pSrc->Width, pSrc->Height, pSrc->X, pSrc->Y)); // // Scale without any crop // SIZE sizeNew; INT NewX = pDest->X, NewY = pDest->Y; WIA_TRACE((TEXT("_ScaleImage: dest before scaling: (%d, %d) @ (%d, %d)"),pDest->Width, pDest->Height, pDest->X, pDest->Y)); sizeNew = PrintScanUtil::ScalePreserveAspectRatio( pDest->Width, pDest->Height, pSrc->Width, pSrc->Height ); NewX += ((pDest->Width - sizeNew.cx) / 2); NewY += ((pDest->Height - sizeNew.cy) / 2); pDest->X = NewX; pDest->Y = NewY; pDest->Width = sizeNew.cx; pDest->Height = sizeNew.cy; WIA_TRACE((TEXT("_ScaleImage: dest after scaling: (%d, %d) @ (%d, %d)"),pDest->Width, pDest->Height, pDest->X, pDest->Y)); return S_OK; } /***************************************************************************** _CropImage Scales src rect to fit into dest rect while preserving aspect ratio *****************************************************************************/ HRESULT _CropImage( Gdiplus::Rect * pSrc, Gdiplus::Rect * pDest ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("_CropImage()"))); if (!pDest || !pSrc) { WIA_ERROR((TEXT("_CropImage: bad params, exiting early!"))); return E_INVALIDARG; } WIA_TRACE((TEXT("_CropImage: pDest before cropping: (%d, %d) @ (%d, %d)"), pDest->Width, pDest->Height, pDest->X, pDest->Y)); // // Scale without any crop // SIZE sizeNew; INT NewX = pSrc->X, NewY = pSrc->Y; WIA_TRACE((TEXT("_CropImage: pSrc before cropping: (%d, %d) @ (%d, %d)"),pSrc->Width, pSrc->Height, pSrc->X, pSrc->Y)); sizeNew = PrintScanUtil::ScalePreserveAspectRatio( pSrc->Width, pSrc->Height, pDest->Width, pDest->Height ); NewX += ((pSrc->Width - sizeNew.cx) / 2); NewY += ((pSrc->Height - sizeNew.cy) / 2); pSrc->X = NewX; pSrc->Y = NewY; pSrc->Width = sizeNew.cx; pSrc->Height = sizeNew.cy; WIA_TRACE((TEXT("_CropImage: pSrc after cropping: (%d, %d) @ (%d, %d)"),pSrc->Width, pSrc->Height, pSrc->X, pSrc->Y)); return S_OK; } /***************************************************************************** _GetImageDimensions Given a GDI+ image object, return the dimensions in the given rectangle... *****************************************************************************/ HRESULT _GetImageDimensions( Gdiplus::Image * pImage, Gdiplus::RectF &rect, Gdiplus::REAL &scalingFactorForY ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("_GetImageDimensions()"))); if (!pImage) { WIA_ERROR((TEXT("_GetImageDimensions: bad params, exiting early!"))); return E_INVALIDARG; } Gdiplus::Unit Unit; HRESULT hr = Gdiplus2HRESULT( pImage->GetBounds( &rect, &Unit ) ); if (FAILED(hr)) { // // Try the old fashioned way... // rect.X = (Gdiplus::REAL)0.0; rect.Y = (Gdiplus::REAL)0.0; rect.Width = (Gdiplus::REAL)pImage->GetWidth(); hr = Gdiplus2HRESULT( pImage->GetLastStatus() ); WIA_CHECK_HR(hr,"_GetImageDimensions: GetWidth failed!"); if (SUCCEEDED(hr)) { rect.Height = (Gdiplus::REAL)pImage->GetHeight(); hr = Gdiplus2HRESULT( pImage->GetLastStatus() ); WIA_CHECK_HR(hr,"_GetImageDimensions: GetHeight failed!"); } } else { if (Unit != Gdiplus::UnitPixel) { hr = S_FALSE; } } Gdiplus::REAL xDPI = pImage->GetHorizontalResolution(); Gdiplus::REAL yDPI = pImage->GetVerticalResolution(); if (yDPI) { scalingFactorForY = xDPI / yDPI; } else { scalingFactorForY = (Gdiplus::REAL)1.0; } WIA_RETURN_HR(hr); } /***************************************************************************** CPhotoItem -- constructors/desctructor *****************************************************************************/ CPhotoItem::CPhotoItem( LPITEMIDLIST pidlFull ) : _pidlFull(NULL), _pImage(NULL), _lFrameCount(-1), _bTimeFrames(FALSE), _pAnnotations(NULL), _pAnnotBits(NULL), _bWeKnowAnnotationsDontExist(FALSE), _pThumbnails(NULL), _cRef(0), _llFileSize(0), _uImageType(DontKnowImageType), _DPIx((Gdiplus::REAL)0.0), _DPIy((Gdiplus::REAL)0.0) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM, TEXT("CPhotoItem::CPhotoItem( fully qualified pidl )"))); if (pidlFull) { _pidlFull = ILClone( pidlFull ); WIA_TRACE((TEXT("_pidlFull = 0x%x"),_pidlFull)); } *_szFileName = 0; // // Get just file name from the pidl // SHFILEINFO fi = {0}; if (SHGetFileInfo( (LPCTSTR)pidlFull, 0, &fi, sizeof(fi), SHGFI_DISPLAYNAME| SHGFI_PIDL )) { StringCchCopy( _szFileName, ARRAYSIZE(_szFileName), fi.szDisplayName ); } } CPhotoItem::~CPhotoItem() { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM, TEXT("CPhotoItem::~CPhotoItem()"))); CAutoCriticalSection lock( _csItem ); // // Free pidl for item // if (_pidlFull) { WIA_TRACE((TEXT("_pidlFull = 0x%x"),_pidlFull)); ILFree( _pidlFull ); _pidlFull = NULL; } // // Free GDI+ icon // if (_pClassBitmap) { delete _pClassBitmap; _pClassBitmap = NULL; } // // Free bitmaps of thumbnails // if (_pThumbnails) { for (INT i=0; i < _lFrameCount; i++) { if (_pThumbnails[i]) { DeleteObject( _pThumbnails[i] ); } } delete _pThumbnails; _pThumbnails = NULL; } // // Destroy GDI+ backing images. This also destroys any // annotation data we have... // _DiscardGdiPlusImages(); } /***************************************************************************** CPhotoItem IUnknown methods *****************************************************************************/ ULONG CPhotoItem::AddRef() { LONG l = InterlockedIncrement(&_cRef); WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS,TEXT("CPhotoItem(0x%x)::AddRef( new count is %d )"),this,l)); if (l < 0) { return 0; } return (ULONG)l; } ULONG CPhotoItem::Release() { LONG l = InterlockedDecrement(&_cRef); WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS,TEXT("CPhotoItem(0x%x)::Release( new count is %d )"),this,l)); if (l > 0) return (ULONG)l; WIA_TRACE((TEXT("deleting object ( this == 0x%x ) because ref count is zero."),this)); delete this; return 0; } ULONG CPhotoItem::ReleaseWithoutDeleting() { LONG l = InterlockedDecrement(&_cRef); WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS,TEXT("CPhotoItem(0x%x)::Release( new count is %d )"),this,l)); return (ULONG)l; } /***************************************************************************** CPhotoItem::GetImageFrameCount returns the number of frames (pages) in this image *****************************************************************************/ HRESULT CPhotoItem::GetImageFrameCount(LONG * pFrameCount) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::GetImageFrameCount(%s)"),_szFileName)); if (!pFrameCount) { return E_INVALIDARG; } HRESULT hr = S_OK; // // Protect us as we go get info about the item... // CAutoCriticalSection lock(_csItem); if (_lFrameCount == -1) { _lFrameCount = 1; // // Ensure the GDI+ image object has been created...this will also // update the frame count... // hr = _CreateGdiPlusImage(); if (SUCCEEDED(hr) && _pImage) { LONG lPageFrames; LONG lTimeFrames; lPageFrames = _pImage->GetFrameCount(&Gdiplus::FrameDimensionPage); lTimeFrames = _pImage->GetFrameCount(&Gdiplus::FrameDimensionTime); if ((lPageFrames > 0) && (lTimeFrames <= 1)) { _lFrameCount = lPageFrames; } else if (lTimeFrames > 0) { // // This is an animated GIF, report only 1 frame... // _lFrameCount = 1; _bTimeFrames = TRUE; } else { _lFrameCount = 1; } } } *pFrameCount = ((_lFrameCount == -1) ? 0 : _lFrameCount); WIA_TRACE((TEXT("%s: returning _FrameCount = %d"),_szFileName,*pFrameCount)); return hr; } /***************************************************************************** CPhotoItem::GetClassBitmap Returns default icon for class (.jpg, .bmp, etc) for this item... *****************************************************************************/ HBITMAP CPhotoItem::GetClassBitmap( const SIZE &sizeDesired ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::GetClassBitmap( %s, size = %d,%d "),_szFileName,sizeDesired.cx, sizeDesired.cy )); HBITMAP hbmReturn = NULL; CAutoCriticalSection lock(_csItem); if (!_pClassBitmap) { // // Get icon from shell // SHFILEINFO fi = {0}; if (SHGetFileInfo( (LPCTSTR)_pidlFull, 0, &fi, sizeof(fi), SHGFI_PIDL | SHGFI_SYSICONINDEX )) { // // Get large (48 x 48) icon image list // IImageList * piml = NULL; if (SUCCEEDED(SHGetImageList( SHIL_EXTRALARGE, IID_IImageList, (void **)&piml )) && piml) { HICON hIcon = NULL; if (SUCCEEDED(piml->GetIcon( fi.iIcon, 0, &hIcon )) && hIcon) { // // Got the ICON, create a bitmap for it... // hbmReturn = WiaUiUtil::CreateIconThumbnail( (HWND)NULL, 50, 60, hIcon, NULL ); if (hbmReturn) { _pClassBitmap = new Gdiplus::Bitmap( hbmReturn, NULL ); DeleteObject( hbmReturn ); hbmReturn = NULL; } DestroyIcon( hIcon ); } piml->Release(); } } } if (_pClassBitmap) { SIZE sizeDrawSize = {0}; // // Scale image to fill thumbnail space while preserving // aspect ratio... // sizeDrawSize = PrintScanUtil::ScalePreserveAspectRatio( sizeDesired.cx, sizeDesired.cy, _pClassBitmap->GetWidth(), _pClassBitmap->GetHeight() ); WIA_TRACE((TEXT("CPhotoItem::GetClassBitmap(%s) - _pClassBitmap( %d, %d )"),_szFileName,_pClassBitmap->GetWidth(), _pClassBitmap->GetHeight())); WIA_TRACE((TEXT("CPhotoItem::GetClassBitmap(%s) - sizeDesired( %d, %d )"),_szFileName,sizeDesired.cx, sizeDesired.cy)); WIA_TRACE((TEXT("CPhotoItem::GetClassBitmap(%s) - sizeDrawsize( %d, %d )"),_szFileName,sizeDrawSize.cx, sizeDrawSize.cy)); Gdiplus::Bitmap * pImage = new Gdiplus::Bitmap( sizeDesired.cx, sizeDesired.cy ); if (pImage) { HRESULT hr = Gdiplus2HRESULT(pImage->GetLastStatus()); if (SUCCEEDED(hr)) { // // Get a graphics to render to // Graphics *pGraphics = Gdiplus::Graphics::FromImage((Gdiplus::Image *)pImage); if (pGraphics) { hr = Gdiplus2HRESULT(pGraphics->GetLastStatus()); // // Make sure it is valid // if (SUCCEEDED(hr)) { // // erase the background of the image // pGraphics->Clear( g_wndColor ); // // Set the interpolation mode to high quality // pGraphics->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBicubic ); // // Set the smoothing (anti-aliasing) mode to high quality as well // pGraphics->SetSmoothingMode( Gdiplus::SmoothingModeHighQuality ); // // Draw scaled image // 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)); hr = Gdiplus2HRESULT(pGraphics->DrawImage( _pClassBitmap, 0 + ((sizeDesired.cx - sizeDrawSize.cx) / 2), 0 + ((sizeDesired.cy - sizeDrawSize.cy) / 2), sizeDrawSize.cx, sizeDrawSize.cy )); WIA_CHECK_HR(hr,"CPhotoItem::GetClassBitmap() - pGraphics->DrawImage( _pClassBitmap ) failed!"); if (SUCCEEDED(hr)) { pImage->GetHBITMAP( g_wndColor, &hbmReturn ); } } // // Clean up our dynamically allocated graphics // delete pGraphics; } else { WIA_ERROR((TEXT("CPhotoItem::GetClassBitmap(%s) - pGraphics was NULL!"),_szFileName)); hr = E_OUTOFMEMORY; } } else { WIA_ERROR((TEXT("CPhotoItem::GetClassBitmap(%s) - pImage failed to be created, hr = 0x%x"),hr)); } delete pImage; } } return hbmReturn; } /***************************************************************************** CPhotoItem::GetThumbnailBitmap Given a DC and a desired size, return an HBITMAP of the thumbnail for a this item. The caller MUST free the HBITMAP returned from this function. *****************************************************************************/ HBITMAP CPhotoItem::GetThumbnailBitmap( const SIZE &sizeDesired, LONG lFrame ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::GetThumbnailBitmap( %s, size = %d,%d "),_szFileName,sizeDesired.cx, sizeDesired.cy )); HBITMAP hbmReturn = NULL; Gdiplus::Image * pImageToUse = NULL; CAutoCriticalSection lock(_csItem); // // Make sure we have a thumbnail image for our photo... // _CreateGdiPlusThumbnail( sizeDesired, lFrame ); if (_pThumbnails && (lFrame < _lFrameCount) && _pThumbnails[lFrame]) { // // Use bitmap to draw with instead of going to the file... // pImageToUse = (Gdiplus::Image *)(Gdiplus::Bitmap::FromHBITMAP( _pThumbnails[lFrame], NULL )); } if (pImageToUse) { WIA_TRACE((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - pImageToUse is (%d x %d)"),_szFileName,pImageToUse->GetWidth(),pImageToUse->GetHeight())); Gdiplus::Bitmap * pImage = new Gdiplus::Bitmap( sizeDesired.cx, sizeDesired.cy ); if (pImage) { HRESULT hr = Gdiplus2HRESULT(pImage->GetLastStatus()); if (SUCCEEDED(hr)) { // // Get a graphics to render to // Graphics *pGraphics = Gdiplus::Graphics::FromImage((Gdiplus::Image *)pImage); if (pGraphics) { hr = Gdiplus2HRESULT(pGraphics->GetLastStatus()); // // Make sure it is valid // if (SUCCEEDED(hr)) { // // compute how to scale the thumbnail image // SIZE sizeDrawSize = {0}; sizeDrawSize = PrintScanUtil::ScalePreserveAspectRatio( sizeDesired.cx, sizeDesired.cy, pImageToUse->GetWidth(), pImageToUse->GetHeight() ); // // erase the background of the image // pGraphics->Clear( g_wndColor ); // // Set the interpolation mode to high quality // pGraphics->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBilinear ); // // Draw scaled image // 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)); hr = Gdiplus2HRESULT(pGraphics->DrawImage( pImageToUse, 0 + ((sizeDesired.cx - sizeDrawSize.cx) / 2), 0 + ((sizeDesired.cy - sizeDrawSize.cy) / 2), sizeDrawSize.cx, sizeDrawSize.cy )); WIA_CHECK_HR(hr,"CPhotoItem::GetThumbnailBitmap() - pGraphics->DrawImage( pImageToUse ) failed!"); if (SUCCEEDED(hr)) { pImage->GetHBITMAP( g_wndColor, &hbmReturn ); } } // // Clean up our dynamically allocated graphics // delete pGraphics; } else { WIA_ERROR((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - pGraphics was NULL!"),_szFileName)); hr = E_OUTOFMEMORY; } } else { WIA_ERROR((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - pImage failed to be created, hr = 0x%x"),hr)); } delete pImage; } // // If we created an image to wrap the bitmap bits, then delete it... // if (pImageToUse) { delete pImageToUse; } } else { WIA_ERROR((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - don't have stored thumbnail bitmap for this image!"),_szFileName)); } return hbmReturn; } /***************************************************************************** CPhotoItem::_DoRotateAnnotations This function requires that the annotation data be already set up and initialized. This is true for the _pImage object as well. This function will not initialize on the fly. *****************************************************************************/ HRESULT CPhotoItem::_DoRotateAnnotations( BOOL bClockwise, UINT Flags ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_DoRotateAnnotations( %s, Flags = 0x%x )"),_szFileName,Flags)); if (!_pAnnotations) { WIA_RETURN_HR(E_INVALIDARG); } if (!_pImage) { WIA_RETURN_HR(E_INVALIDARG); } HRESULT hr; Gdiplus::REAL scaleY; // // Get width & height of backing image... // Gdiplus::RectF rectBounds; hr = _GetImageDimensions( _pImage, rectBounds, scaleY ); if (SUCCEEDED(hr)) { INT i = 0; CAnnotation * pA = NULL; INT iNewW = 0, iNewH = 0; if ((Flags & RF_USE_THUMBNAIL_DATA) || (Flags & RF_USE_MEDIUM_QUALITY_DATA)) { // // We flip here, because in the main _DoHandleRotation we only // rotated the thumbnail data, so the backing image width & height // haven't changed. It will be changed when we rotate to print, however, // so feed the correct values to the annotation rotate code... // iNewW = (INT)rectBounds.Height; iNewH = (INT)rectBounds.Width; } else { iNewW = (INT)rectBounds.Width; iNewH = (INT)rectBounds.Height; } WIA_TRACE((TEXT("CPhotoItem::_DoRotateAnnotations - bClockwise = %d, new width = %d, new height = %d"),bClockwise,iNewW,iNewH)); // // rotate all the annotations // do { pA = _pAnnotations->GetAnnotation(i++); if (pA) { pA->Rotate( iNewW, iNewH, bClockwise ); } } while( pA ); } WIA_RETURN_HR(hr); } #define DO_CONVERT_GDIPLUS_STATUS(hr,status) if ( (status == Gdiplus::Ok) || \ (status == Gdiplus::OutOfMemory) || \ (status == Gdiplus::ObjectBusy) || \ (status == Gdiplus::FileNotFound) || \ (status == Gdiplus::AccessDenied) || \ (status == Gdiplus::Win32Error) \ ) \ { \ hr = Gdiplus2HRESULT( status ); \ }\ else \ {\ WIA_TRACE((TEXT("Mapping Gdiplus error %d to PPW_E_UNABLE_TO_ROTATE"),status));\ hr = PPW_E_UNABLE_TO_ROTATE;\ } /***************************************************************************** CPhotoItem::_DoHandleRotation Handle rotating the image to render if/when needed or specified... *****************************************************************************/ HRESULT CPhotoItem::_DoHandleRotation( Gdiplus::Image * pImage, Gdiplus::Rect &src, Gdiplus::Rect * pDest, UINT Flags, Gdiplus::REAL &ScaleFactorForY ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_DoHandleRotation( %s, Flags = 0x%x )"),_szFileName,Flags)); HRESULT hr = S_OK; Gdiplus::GpStatus status = Gdiplus::Ok; if (Flags & RF_ROTATION_MASK) { WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - A rotation flag was specified"),_szFileName)); if (Flags & RF_ROTATE_AS_NEEDED) { WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - RF_ROTATE_AS_NEEDED was specified"),_szFileName)); // // If the source and destination aspect ratios are on the opposite sides of 1.0, // rotate the image 90 degrees // const DOUBLE srcAspect = (DOUBLE)src.Width / (DOUBLE)src.Height; const DOUBLE destAspect = (DOUBLE)pDest->Width / (DOUBLE)pDest->Height; if((srcAspect >= (DOUBLE)1.0) ^ (destAspect >= (DOUBLE)1.0)) { // // Rotate the image as needed... // if (Flags & RF_ROTATE_270) { WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - Rotating Image 270 degrees"),_szFileName)); status = pImage->RotateFlip( Gdiplus::Rotate270FlipNone ); if (status == Gdiplus::Ok) { _DoRotateAnnotations( FALSE, Flags ); } } else { WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - Rotating Image 90 degrees"),_szFileName)); status = pImage->RotateFlip( Gdiplus::Rotate90FlipNone ); if (status == Gdiplus::Ok) { _DoRotateAnnotations( TRUE, Flags ); } } // // Map most of these error codes to UNABLE_TO_ROTATE... // DO_CONVERT_GDIPLUS_STATUS(hr,status) } } else { // // Rotate the image... // if (Flags & RF_ROTATE_90) { WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - Rotating Image 90 degrees"),_szFileName)); status = pImage->RotateFlip( Gdiplus::Rotate90FlipNone ); if (status == Gdiplus::Ok) { _DoRotateAnnotations( TRUE, Flags ); } } else if (Flags & RF_ROTATE_180) { WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - Rotating Image 180 degrees"),_szFileName)); status = pImage->RotateFlip( Gdiplus::Rotate180FlipNone ); if (status == Gdiplus::Ok) { // // Rotate 90 degrees twice... // _DoRotateAnnotations( TRUE, Flags ); _DoRotateAnnotations( TRUE, Flags ); } } else if (Flags & RF_ROTATE_270) { WIA_TRACE((TEXT("CPhotoItem::_DoHandleRotation(%s) - Rotating Image 270 degrees"),_szFileName)); status = pImage->RotateFlip( Gdiplus::Rotate270FlipNone ); if (status == Gdiplus::Ok) { _DoRotateAnnotations( FALSE, Flags ); } } else { status = Gdiplus::Ok; } // // Map most of these error codes to UNABLE_TO_ROTATE... // DO_CONVERT_GDIPLUS_STATUS(hr,status); } // // If we were able to rotate the image, then update the source rectangle // to make sure it still reflects reality... // if (SUCCEEDED(hr)) { Gdiplus::RectF rectBounds; hr = _GetImageDimensions( pImage, rectBounds, ScaleFactorForY ); if (SUCCEEDED(hr)) { src.Width = (INT)rectBounds.Width; src.Height = (INT)(rectBounds.Height * ScaleFactorForY); src.X = (INT)rectBounds.X; src.Y = (INT)(rectBounds.Y * ScaleFactorForY); } else { src.Width = 0; src.Height = 0; src.X = 0; src.Y = 0; } } } if (Flags & RF_NO_ERRORS_ON_FAILURE_TO_ROTATE) { WIA_RETURN_HR(S_OK); } WIA_RETURN_HR(hr); } /***************************************************************************** CPhotoItem::_RenderAnnotations If annotations exist, then render them on top of this image... *****************************************************************************/ HRESULT CPhotoItem::_RenderAnnotations( HDC hDC, RENDER_DIMENSIONS * pDim, Gdiplus::Rect * pDest, Gdiplus::Rect &src, Gdiplus::Rect &srcAfterClipping ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_RenderAnnotations(%s)"),_szFileName)); if (!_pAnnotations || !hDC) { WIA_RETURN_HR(E_INVALIDARG); } // // Save the settings for this DC... // INT iSavedDC = SaveDC( hDC ); // // setup the destination DC: // SetMapMode(hDC, MM_TEXT); SetStretchBltMode(hDC, COLORONCOLOR); WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - dest is (%d,%d) @ (%d,%d)"),_szFileName,pDest->Width,pDest->Height,pDest->X,pDest->Y)); 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)); WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - NominalPhysicalSize is (%d,%d)"),_szFileName,pDim->NominalPhysicalSize.cx,pDim->NominalPhysicalSize.cy)); WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - NominalDevicePrintArea is (%d,%d)"),_szFileName,pDim->NominalDevicePrintArea.cx,pDim->NominalDevicePrintArea.cy)); // // Get device rect // Gdiplus::RectF rectDevice; rectDevice.X = (Gdiplus::REAL)pDim->rcDevice.left; rectDevice.Y = (Gdiplus::REAL)pDim->rcDevice.top; rectDevice.Width = (Gdiplus::REAL)(pDim->rcDevice.right - pDim->rcDevice.left); rectDevice.Height = (Gdiplus::REAL)(pDim->rcDevice.bottom - pDim->rcDevice.top); // // Compute LPtoDP scaling factors // Gdiplus::REAL xLPtoDP = 0.0; Gdiplus::REAL yLPtoDP = 0.0; if (pDim->bDeviceIsScreen) { xLPtoDP = rectDevice.Width / (Gdiplus::REAL)pDim->NominalPhysicalSize.cx; yLPtoDP = rectDevice.Height / (Gdiplus::REAL)pDim->NominalPhysicalSize.cy; } else { xLPtoDP = rectDevice.Width / (Gdiplus::REAL)pDim->NominalDevicePrintArea.cx; yLPtoDP = rectDevice.Height / (Gdiplus::REAL)pDim->NominalDevicePrintArea.cy; } // // Get destination rect in device coords... // Gdiplus::RectF rectDest; rectDest.X = pDest->X * xLPtoDP; rectDest.Y = pDest->Y * xLPtoDP; rectDest.Width = pDest->Width * xLPtoDP; rectDest.Height = pDest->Height * xLPtoDP; WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - original source rect is (%d, %d) @ (%d, %d)"),_szFileName,src.Width,src.Height,src.X,src.Y)); WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - clipped source rect is (%d, %d) @ (%d, %d)"),_szFileName,srcAfterClipping.Width,srcAfterClipping.Height,srcAfterClipping.X,srcAfterClipping.Y)); 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)); // // dx & dy represent how much bigger a destination rectangle would be // for the whole image, rather than the copped image... // Gdiplus::REAL dx = (Gdiplus::REAL)(src.Width - srcAfterClipping.Width) * (rectDest.Width / (Gdiplus::REAL)srcAfterClipping.Width); Gdiplus::REAL dy = (Gdiplus::REAL)(src.Height - srcAfterClipping.Height) * (rectDest.Height / (Gdiplus::REAL)srcAfterClipping.Height); WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - dx = %d dy = %d"),_szFileName,(INT)dx,(INT)dy)); // // Set the clipping rectangle on the device hDC in device coords... // RECT rcClip; rcClip.left = (INT)rectDest.X; rcClip.right = rcClip.left + (INT)rectDest.Width; rcClip.top = (INT)rectDest.Y; rcClip.bottom = rcClip.top + (INT)rectDest.Height; #ifdef SHOW_ANNOT_RECTS { 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)); HBRUSH hbr = CreateSolidBrush( RGB( 0xFF, 0x00, 0x00 ) ); FrameRect( hDC, &rcClip, hbr ); DeleteObject( (HGDIOBJ)hbr ); } #endif HRGN hrgn = CreateRectRgnIndirect(&rcClip); if (hrgn != NULL) { WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - setting clip region to (%d, %d, %d, %d)"),_szFileName,rcClip.left, rcClip.top, rcClip.right, rcClip.bottom)); SelectClipRgn(hDC, hrgn); } // // Make dest rect for whole image, knowing we will clip later... // rectDest.X -= (dx / (Gdiplus::REAL)2.0); rectDest.Y -= (dy / (Gdiplus::REAL)2.0); rectDest.Width += dx; rectDest.Height += dy; 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)); #ifdef SHOW_ANNOT_RECTS { RECT rc; rc.left = (INT)rectDest.X; rc.top = (INT)rectDest.Y; rc.right = rc.left + (INT)rectDest.Width; rc.bottom = rc.top + (INT)rectDest.Height; HBRUSH hbr = CreateSolidBrush( RGB( 0x00, 0xFF, 0x00 ) ); FrameRect( hDC, &rc, hbr ); DeleteObject( (HGDIOBJ)hbr ); } #endif // // set up mapping modes for annotations // SetMapMode(hDC, MM_ANISOTROPIC); // // Set window org/ext to entire image... // SetWindowOrgEx(hDC, src.X, src.Y, NULL); WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - set window org to (%d,%d)"),_szFileName,src.X,src.Y)); SetWindowExtEx(hDC, src.Width, src.Height, NULL); WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - set window ext to (%d,%d)"),_szFileName,src.Width,src.Height)); // // Set the viewport to be at the corner of the image we are trying // to draw annotations for... // SetViewportOrgEx( hDC, (INT)rectDest.X, (INT)rectDest.Y, NULL ); WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - set viewport org to (%d,%d)"),_szFileName,(INT)rectDest.X,(INT)rectDest.Y)); // // We need to set scaling mode of image to dest rect // SetViewportExtEx( hDC, (INT)rectDest.Width, (INT)rectDest.Height, NULL ); WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - set viewport ext to (%d,%d)"),_szFileName,(INT)rectDest.Width, (INT)rectDest.Height)); // // Now that everything is set up, render the annotations... // WIA_TRACE((TEXT("CPhotoItem::_RenderAnnotations(%s) - calling RenderAllMarks(0x%x)"),_szFileName,hDC)); _pAnnotations->RenderAllMarks(hDC); SelectClipRgn(hDC, NULL); if (hrgn != NULL) DeleteObject(hrgn); if (iSavedDC) { RestoreDC( hDC, iSavedDC ); } WIA_RETURN_HR(S_OK); } /***************************************************************************** CPhotoItem::_MungeAnnotationDataForThumbnails If we're rendering using thumbnails, then we need to munge some data so that we will render correctly... *****************************************************************************/ HRESULT CPhotoItem::_MungeAnnotationDataForThumbnails( Gdiplus::Rect &src, Gdiplus::Rect &srcBeforeClipping, Gdiplus::Rect * pDest, UINT Flags ) { WIA_TRACE((TEXT("CPhotoItem::_MungeAnnotationDataForThumbnails(%s)"),_szFileName)); HRESULT hr = _CreateGdiPlusImage(); if (FAILED(hr)) { WIA_RETURN_HR(hr); } if (!_pImage) { WIA_RETURN_HR(E_FAIL); } // // we need to construct the original image rectangle appropriate for // annotation use... // Gdiplus::RectF rectImage; Gdiplus::REAL scaleY; hr = _GetImageDimensions( _pImage, rectImage, scaleY ); if (FAILED(hr)) { // // If we couldn't accurately get the image dimensions, then bail... // return hr; } // // Scale image if it's non-square pixels // if (scaleY != (Gdiplus::REAL)0.0) { rectImage.Height *= scaleY; rectImage.Y *= scaleY; } 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)); // // Now, do all the transforms on the real image rectangle... // const DOUBLE srcAspect = (DOUBLE)rectImage.Width / (DOUBLE)rectImage.Height; const DOUBLE destAspect = (DOUBLE)pDest->Width / (DOUBLE)pDest->Height; if((srcAspect >= (DOUBLE)1.0) ^ (destAspect >= (DOUBLE)1.0)) { // // Image needs to be rotated, swap width & height // rectImage.X = rectImage.Width; rectImage.Width = rectImage.Height; rectImage.Height = rectImage.X; rectImage.X = 0.0; } src.X = (INT)rectImage.X; src.Y = (INT)rectImage.Y; src.Width = (INT)rectImage.Width; src.Height = (INT)rectImage.Height; WIA_TRACE((TEXT("CPhotoItem::_Munge(%s) - srcRect after rotation is (%d,%d) @ (%d,%d)"),_szFileName,src.Width,src.Height,src.X,src.Y)); srcBeforeClipping = src; if (Flags & RF_CROP_TO_FIT) { hr = _CropImage( &src, pDest ); } else if (Flags & RF_SCALE_TO_FIT) { hr = _ScaleImage( &src, pDest ); } // // Unscale the src rect // if (scaleY != (Gdiplus::REAL)0.0) { src.Height = (INT)(((Gdiplus::REAL)src.Height) / scaleY); src.Y = (INT)(((Gdiplus::REAL)src.Y) / scaleY); srcBeforeClipping.Height = (INT)(((Gdiplus::REAL)srcBeforeClipping.Height) / scaleY); srcBeforeClipping.Y = (INT)(((Gdiplus::REAL)srcBeforeClipping.Y) / scaleY); } WIA_RETURN_HR(hr); } /***************************************************************************** CPhotoItem::_GetThumbnailQualityImage Returns in ppImage a pointer to an image class. If pbNeedsToBeDeleted then the caller must call delete on the returned pImage. *****************************************************************************/ HRESULT CPhotoItem::_GetThumbnailQualityImage( Gdiplus::Image ** ppImage, RENDER_OPTIONS * pRO, BOOL * pbNeedsToBeDeleted ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_GetThumbnailQualityImage( %s )"),_szFileName)); if (!ppImage || !pRO || !pbNeedsToBeDeleted) { WIA_ERROR((TEXT("CPhotoItem::_GetThumbnailQualityImage( %s ) - returning E_INVALIDARG!"),_szFileName)); return E_INVALIDARG; } // // Initialize incoming params // *ppImage = NULL; *pbNeedsToBeDeleted = FALSE; // // Make sure we have a GDI+ image class for our thumbnail... // SIZE sizeDesired = { DEFAULT_THUMB_WIDTH, DEFAULT_THUMB_HEIGHT }; HRESULT hr = _CreateGdiPlusThumbnail( sizeDesired, pRO->lFrame ); if (SUCCEEDED(hr) && (NULL!=_pThumbnails) && (pRO->lFrame < _lFrameCount) && (NULL!=_pThumbnails[pRO->lFrame])) { // // If we already have thumbnail bits, then use those by creating // a GDI+ bitmap class over those bits... // *ppImage = Gdiplus::Bitmap::FromHBITMAP( _pThumbnails[pRO->lFrame], NULL ); if (*ppImage) { hr = Gdiplus2HRESULT((*ppImage)->GetLastStatus()); WIA_TRACE((TEXT("CPhotoItem::_GetThumbnailQualityImage(%s) -- pImage created from thumbnail data is sized as (%d x %d)"),_szFileName,(*ppImage)->GetWidth(),(*ppImage)->GetHeight())); if (SUCCEEDED(hr)) { *pbNeedsToBeDeleted = TRUE; } else { delete (*ppImage); *ppImage = NULL; } } else { hr = E_OUTOFMEMORY; } } else { WIA_ERROR((TEXT("CPhotoItem::_GetThumbnailQualityImage(%s) -- no thumbnail exists (_pThumbnails=0x%x, lFrame = %d, _lFrameCount = %d)"),_szFileName,_pThumbnails,pRO->lFrame,_lFrameCount)); } WIA_RETURN_HR(hr); } /***************************************************************************** CPhotoItem::_GetMediumQualityImage Returns in ppImage a pointer to an image class. If pbNeedsToBeDeleted then the caller must call delete on the returned pImage. *****************************************************************************/ HRESULT CPhotoItem::_GetMediumQualityImage( Gdiplus::Image ** ppImage, RENDER_OPTIONS * pRO, BOOL * pbNeedsToBeDeleted ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_GetMediumQualityImage( %s )"),_szFileName)); if (!ppImage || !pRO || !pbNeedsToBeDeleted) { WIA_ERROR((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - returning E_INVALIDARG!"),_szFileName)); return E_INVALIDARG; } // // Initialize incoming params // *ppImage = NULL; *pbNeedsToBeDeleted = FALSE; // // We want to use the full high-res image. // Make sure we have a GDI+ image class for our photo... // HRESULT hr = _CreateGdiPlusImage(); if (SUCCEEDED(hr) && _pImage) { // // If this a metafile type of image, just use the original image // GUID guidFormat = {0}; hr = Gdiplus2HRESULT(_pImage->GetRawFormat(&guidFormat)); if ( (SUCCEEDED(hr) && (guidFormat == ImageFormatIcon)) || (Gdiplus::ImageTypeMetafile == _pImage->GetType()) ) { WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - this is a metafile or an icon, using the full image..."),_szFileName)); hr = Gdiplus2HRESULT(_pImage->SelectActiveFrame( _bTimeFrames ? &Gdiplus::FrameDimensionTime : &Gdiplus::FrameDimensionPage, pRO->lFrame )); *ppImage = _pImage; } else { // // Select the specified page // hr = Gdiplus2HRESULT(_pImage->SelectActiveFrame( _bTimeFrames ? &Gdiplus::FrameDimensionTime : &Gdiplus::FrameDimensionPage, pRO->lFrame )); WIA_CHECK_HR(hr,"CPhotoItem::_GetMediumQualityImage() - couldn't select frame!"); if (SUCCEEDED(hr)) { // // Here's the algoritm to decide how big an image to create. // // (1) At least thumbnail size (120x120) // (2) Attempt to scale to either 150dpi or 180dpi, depending on X DPI resolution of the printer // INT xDPI = 0, yDPI = 0; if ((pRO->Dim.DPI.cx % 150) == 0) { // // DPI is some even multiple of 150 (i.e., 150, 300, 600, 1200, 2400, etc) // xDPI = 150; yDPI = MulDiv( pRO->Dim.DPI.cy, xDPI, pRO->Dim.DPI.cx ); } else { // // DPI is some even multiple of 180 (i.e., 180, 360, 720, 1440, 2880, etc) // xDPI = 180; yDPI = MulDiv( pRO->Dim.DPI.cy, xDPI, pRO->Dim.DPI.cx ); } WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - scaling to xDPI=%d yDPI=%d"),_szFileName,xDPI,yDPI)); // // Handle the error case of trying to scale yDPI // if (yDPI <= 0) { yDPI = xDPI; WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - fixing up yDPI to be %d"),_szFileName,yDPI)); } // // Figure out the desired size of the new image... // INT Width = MulDiv( pRO->pDest->Width, xDPI, 10000 ); INT Height = MulDiv( pRO->pDest->Height, yDPI, 10000 ); WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - desired size of image is (%d x %d)"),_szFileName,Width,Height)); if ((Width < DEFAULT_THUMB_WIDTH) && (Height < DEFAULT_THUMB_HEIGHT)) { Width = 120; Height = 120; WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - desired size of image is smaller than thumbnail, making it thumbnail size (%d x %d)"),_szFileName,Width,Height)); } // // Now we now what size we're trying to scale to, create an image scaled (without cropping) to that size... // Gdiplus::RectF rectImage; Gdiplus::REAL scaleY; if (SUCCEEDED(_GetImageDimensions( _pImage, rectImage, scaleY ))) { // // scale for non-square pixels... // if (scaleY != (Gdiplus::REAL)0.0) { rectImage.Height *= scaleY; rectImage.Y *= scaleY; } SIZE sizeDrawSize = {0}; sizeDrawSize = PrintScanUtil::ScalePreserveAspectRatio( Width, Height, (INT)rectImage.Width, (INT)rectImage.Height ); WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - size of full image( %d x %d )"),_szFileName, (INT)rectImage.Width, (INT)rectImage.Height)); WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - sizeDesired( %d x %d )"),_szFileName, Width, Height)); WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - sizeDrawsize( %d x %d )"),_szFileName, sizeDrawSize.cx, sizeDrawSize.cy)); // // Create the target bitmap and make sure it succeeded // *ppImage = (Gdiplus::Image *)new Gdiplus::Bitmap( sizeDrawSize.cx, sizeDrawSize.cy ); if (*ppImage) { hr = Gdiplus2HRESULT((*ppImage)->GetLastStatus()); if (SUCCEEDED(hr)) { // // Set the resolution (DPI) for the bitmap // ((Gdiplus::Bitmap *)(*ppImage))->SetResolution( (Gdiplus::REAL)xDPI, (Gdiplus::REAL)yDPI ); // // Get a graphics to render to // Graphics *pGraphics = Gdiplus::Graphics::FromImage(*ppImage); if (pGraphics) { hr = Gdiplus2HRESULT(pGraphics->GetLastStatus()); // // Make sure it is valid // if (SUCCEEDED(hr)) { // // Set the interpolation mode to high quality // pGraphics->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBicubic ); // // Set the smoothing (anti-aliasing) mode to high quality as well // pGraphics->SetSmoothingMode( Gdiplus::SmoothingModeHighQuality ); // // Draw scaled image // WIA_TRACE((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - calling pGraphics->DrawImage( _pImage, 0, 0, %d, %d )"),_szFileName,sizeDrawSize.cx,sizeDrawSize.cy)); Gdiplus::Rect rectDest; rectDest.X = 0; rectDest.Y = 0; rectDest.Width = sizeDrawSize.cx; rectDest.Height = sizeDrawSize.cy; Gdiplus::ImageAttributes imageAttr; imageAttr.SetWrapMode( Gdiplus::WrapModeTileFlipXY, Gdiplus::Color(), FALSE ); // // Undo scaling // if (scaleY != (Gdiplus::REAL)0.0) { rectImage.Height /= scaleY; rectImage.Y /= scaleY; } // // Finally render the image w/the right settings // pGraphics->DrawImage( _pImage, rectDest, 0, 0, (INT)rectImage.Width, (INT)rectImage.Height, Gdiplus::UnitPixel, &imageAttr ); WIA_CHECK_HR(hr,"CPhotoItem::_GetMediumQualityImage() - pGraphics->DrawImage( _pImage, 0, 0, sizeDrawSize.cx, sizeDrawSize.cy ) failed!"); if (SUCCEEDED(hr)) { *pbNeedsToBeDeleted = TRUE; } else { delete (*ppImage); *ppImage = NULL; } } // // Clean up our dynamically allocated graphics // delete pGraphics; } else { WIA_ERROR((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - pGraphics was NULL!"),_szFileName)); hr = E_OUTOFMEMORY; } } else { delete (*ppImage); *ppImage = NULL; } } else { WIA_ERROR((TEXT("CPhotoItem::_GetMediumQualityImage(%s) - failed to create new pImage for medium quality data!"),_szFileName)); hr = E_OUTOFMEMORY; } } } } } WIA_RETURN_HR(hr); } /***************************************************************************** CPhotoItem::_GetFullQualityImage Returns in ppImage a pointer to an image class. If pbNeedsToBeDeleted then the caller must call delete on the returned pImage. *****************************************************************************/ HRESULT CPhotoItem::_GetFullQualityImage( Gdiplus::Image ** ppImage, RENDER_OPTIONS * pRO, BOOL * pbNeedsToBeDeleted ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_GetFullQualityImage( %s )"),_szFileName)); if (!ppImage || !pbNeedsToBeDeleted) { WIA_ERROR((TEXT("CPhotoItem::_GetFullQualityImage(%s) - returning E_INVALIDARG!"),_szFileName)); return E_INVALIDARG; } // // Initialize incoming params // *ppImage = NULL; *pbNeedsToBeDeleted = FALSE; // // We want to use the full high-res image. // Make sure we have a GDI+ image class for our photo... // HRESULT hr = _CreateGdiPlusImage(); if (SUCCEEDED(hr) && _pImage) { // // Select the specified page // hr = Gdiplus2HRESULT(_pImage->SelectActiveFrame( _bTimeFrames ? &Gdiplus::FrameDimensionTime : &Gdiplus::FrameDimensionPage, pRO->lFrame )); if (SUCCEEDED(hr)) { *ppImage = _pImage; WIA_TRACE((TEXT("CPhotoItem::_GetFullQualityImage(%s) -- *ppImage created from full image data is sized as (%d x %d)"),_szFileName,_pImage->GetWidth(),_pImage->GetHeight())); } else { WIA_ERROR((TEXT("CPhotoItem::_GetFullQualityImage(%s) - couldn't select frame %d, hr = 0x%x"),_szFileName,pRO->lFrame,hr)); } } WIA_RETURN_HR(hr); } #define CHECK_AND_EXIT_ON_FAILURE(hr) if (FAILED(hr)) {if (pImage && (pImage!=_pImage)) {delete pImage;} WIA_RETURN_HR(hr);} /***************************************************************************** CPhotoItem::Render Renders the given item into the Graphics that is supplied... *****************************************************************************/ HRESULT CPhotoItem::Render( RENDER_OPTIONS * pRO ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::Render( %s, pRO = 0x%x)"),_szFileName,pRO)); if (!pRO) { WIA_ERROR((TEXT("CPhotoItem::Render(%s) - pRO is NULL, don't have any input!"),_szFileName)); return E_INVALIDARG; } WIA_TRACE((TEXT("CPhotoItem::Render(%s) - Render Options were specificed as:"),_szFileName)); WIA_TRACE((TEXT("CPhotoItem::Render(%s) - g = 0x%x"),_szFileName,pRO->g)); 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)); WIA_TRACE((TEXT("CPhotoItem::Render(%s) - Flags = 0x%x"),_szFileName,pRO->Flags)); WIA_TRACE((TEXT("CPhotoItem::Render(%s) - lFrame = %d"),_szFileName,pRO->lFrame)); HRESULT hr = S_OK; Gdiplus::Image * pImage = NULL; BOOL bNeedsToBeDeleted = FALSE; Gdiplus::GpStatus status; // // Check for bad args... // if (!pRO->g) { WIA_ERROR((TEXT("CPhotoItem::Render(%s) - g is NULL, can't draw anything"),_szFileName)); return E_INVALIDARG; } if ((pRO->Flags & RF_STRETCH_TO_FIT) && (pRO->Flags & (RF_CROP_TO_FIT | RF_SCALE_TO_FIT))) { WIA_ERROR((TEXT("CPhotoItem::Render(%s) - RF_STRETCH_TO_FIT can't be combined with CROP or SCALE"),_szFileName)); return E_INVALIDARG; } CAutoCriticalSection lock(_csItem); // // Refresh annotation data if we have it // _LoadAnnotations(); if (pRO->Flags & RF_USE_THUMBNAIL_DATA) { WIA_TRACE((TEXT("CPhotoItem::Render(%s) -- render using thumbnail data..."),_szFileName)); hr = _GetThumbnailQualityImage( &pImage, pRO, &bNeedsToBeDeleted ); } else if (pRO->Flags & RF_USE_MEDIUM_QUALITY_DATA) { WIA_TRACE((TEXT("CPhotoItem::Render(%s) -- render using high quality thumbnail data..."),_szFileName)); hr = _GetMediumQualityImage( &pImage, pRO, &bNeedsToBeDeleted ); } else if (pRO->Flags & RF_USE_FULL_IMAGE_DATA) { WIA_TRACE((TEXT("CPhotoItem::Render(%s) -- render using full image data..."),_szFileName)); hr = _GetFullQualityImage( &pImage, pRO, &bNeedsToBeDeleted ); } else { WIA_ERROR((TEXT("CPhotoItem::Render(%s) -- bad render data flags"),_szFileName)); WIA_RETURN_HR(E_INVALIDARG); } CHECK_AND_EXIT_ON_FAILURE(hr); // // We've constructed the appropriate image, now try to load the annotations... // if (_pAnnotBits && _pAnnotBits[pRO->lFrame] && _pAnnotations && _pImage) { _pAnnotations->BuildAllMarksFromData( _pAnnotBits[pRO->lFrame]->value, _pAnnotBits[pRO->lFrame]->length, (ULONG)_pImage->GetHorizontalResolution(), (ULONG)_pImage->GetVerticalResolution() ); WIA_TRACE((TEXT("CPhotoItem::Render(%s) -- %d annotation marks for frame %d found and initialized"),_szFileName,_pAnnotations->GetCount(),pRO->lFrame)); } // // Get the dimensions of the source image... // Gdiplus::Rect src; // // Do this so EMF/WMF print and draw correctly... // Gdiplus::RectF rectBounds; Gdiplus::REAL scaleY; hr = _GetImageDimensions( pImage, rectBounds, scaleY ); if (SUCCEEDED(hr)) { src.Width = (INT)rectBounds.Width; src.Height = (INT)(rectBounds.Height * scaleY); src.X = (INT)rectBounds.X; src.Y = (INT)(rectBounds.Y * scaleY); } CHECK_AND_EXIT_ON_FAILURE(hr); WIA_TRACE((TEXT("CPhotoItem::Render(%s) - srcRect is (%d,%d) @ (%d,%d) before any changes"),_szFileName,src.Width, src.Height, src.X, src.Y)); // // do any needed rotation // hr = _DoHandleRotation( pImage, src, pRO->pDest, pRO->Flags, scaleY ); CHECK_AND_EXIT_ON_FAILURE(hr); 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)); 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)); // // If things are still good, do croping/scaling and then draw the image... // // First check if we should crop... // Gdiplus::Rect srcBeforeClipping = src; if (pRO->Flags & (RF_CROP_TO_FIT | RF_SCALE_TO_FIT)) { #ifdef DEBUG if (pRO->Flags & RF_CROP_TO_FIT) { WIA_TRACE((TEXT("CPhotoItem::Render(%s) - RF_CROP_TO_FIT was specified"),_szFileName)); } if (pRO->Flags & RF_SCALE_TO_FIT) { WIA_TRACE((TEXT("CPhotoItem::Render(%s) - RF_SCALE_TO_FIT was specified"),_szFileName)); } #endif if (pRO->Flags & RF_CROP_TO_FIT) { hr = _CropImage( &src, pRO->pDest ); } else if (pRO->Flags & RF_SCALE_TO_FIT) { hr = _ScaleImage( &src, pRO->pDest ); } else { WIA_ERROR((TEXT("CPhotoItem::Render(%s) - CropScale: unknown configuration"),_szFileName)); hr = E_FAIL; } } WIA_TRACE((TEXT("CPhotoItem::Render(%s) - srcRect is (%d,%d) @ (%d,%d) after scaling"),_szFileName,src.Width, src.Height, src.X, src.Y)); 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)); CHECK_AND_EXIT_ON_FAILURE(hr); // // set the destination rectangle... // Gdiplus::Rect destTemp( pRO->pDest->X, pRO->pDest->Y, pRO->pDest->Width, pRO->pDest->Height ); // // If this is a non-square pixel image, we need to reset the source rectangle to be back to actual // pixels, instead of incorporating DPI as well... // if ((scaleY != (Gdiplus::REAL)0.0) && (scaleY != (Gdiplus::REAL)1.0)) { src.Height = (INT)((Gdiplus::REAL)src.Height / scaleY); src.Y = (INT)((Gdiplus::REAL)src.Y / scaleY); srcBeforeClipping.Height = (INT)((Gdiplus::REAL)srcBeforeClipping.Height / scaleY); srcBeforeClipping.Y = (INT)((Gdiplus::REAL)srcBeforeClipping.Y / scaleY); } // // Set the interpolation mode to high quality // pRO->g->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBicubic ); // // Set the smoothing (anti-aliasing) mode to high quality as well // pRO->g->SetSmoothingMode( Gdiplus::SmoothingModeHighQuality ); // // Set the wrap mode // Gdiplus::ImageAttributes imageAttr; imageAttr.SetWrapMode( Gdiplus::WrapModeTileFlipXY, Gdiplus::Color(), FALSE ); // // Time to draw the image. // 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)); status = pRO->g->DrawImage( pImage, destTemp, src.X, src.Y, src.Width, src.Height, Gdiplus::UnitPixel, &imageAttr ); // // Check for errors, and then draw annotations... // hr = Gdiplus2HRESULT(status); WIA_CHECK_HR(hr,"CPhotoItem::Render() - g->DrawImage( pImage ) failed!"); // // Render annotations if they exist... // if (_pAnnotations && _pAnnotBits && _pAnnotBits[pRO->lFrame]) { if ((pRO->Flags & RF_USE_THUMBNAIL_DATA) || (pRO->Flags & RF_USE_MEDIUM_QUALITY_DATA)) { _MungeAnnotationDataForThumbnails( src, srcBeforeClipping, pRO->pDest, pRO->Flags ); } HDC hdcTemp = pRO->g->GetHDC(); if ((Gdiplus::Ok == pRO->g->GetLastStatus()) && hdcTemp) { _RenderAnnotations( hdcTemp, &pRO->Dim, pRO->pDest, srcBeforeClipping, src ); pRO->g->ReleaseHDC( hdcTemp ); } } // // If we created a new object for the image bits then delete it here... // if (bNeedsToBeDeleted) { delete pImage; } // // To save memory, once we have rendered the full image we discard it // so the memory can be reclaimed... // if ((pRO->Flags & RF_USE_FULL_IMAGE_DATA) || (pRO->Flags & RF_USE_MEDIUM_QUALITY_DATA)) { _DiscardGdiPlusImages(); } WIA_RETURN_HR(hr); } /***************************************************************************** CPhotoItem::_LoadAnnotations If there are annotations in this image, load them... *****************************************************************************/ HRESULT CPhotoItem::_LoadAnnotations() { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_LoadAnnotations(%s)"),_szFileName)); // // Only do this if we don't already have the data and haven't already // tried to load this before and found out there are no annotations... // if (!_pAnnotBits && !_pAnnotations && !_bWeKnowAnnotationsDontExist) { // // Ensure we have the image... // _CreateGdiPlusImage(); // // Make sure we have frame data // LONG lDummy = 0; GetImageFrameCount( &lDummy ); // // If we have any annotations, then load them up accross all the frames... // UINT uSize = 0; BOOL bHasAnnotations = FALSE; Gdiplus::Status status; _pAnnotations = (CAnnotationSet *)new CAnnotationSet(); if (_pAnnotations) { _pAnnotBits = (Gdiplus::PropertyItem **) new BYTE[ sizeof(LPVOID) * _lFrameCount ]; if (_pAnnotBits) { for (LONG lCurFrame=0; lCurFrame < _lFrameCount; lCurFrame++) { status = _pImage->SelectActiveFrame( _bTimeFrames ? &Gdiplus::FrameDimensionTime : &Gdiplus::FrameDimensionPage, lCurFrame ); if (Gdiplus::Ok == status) { // // Load the annotation bits for this frame... // uSize = _pImage->GetPropertyItemSize( ANNOTATION_IMAGE_TAG ); if (uSize > 0) { _pAnnotBits[lCurFrame] = (Gdiplus::PropertyItem *) new BYTE[ uSize ]; if (_pAnnotBits[lCurFrame]) { // // Read the annotations tag from the file... // status = _pImage->GetPropertyItem( ANNOTATION_IMAGE_TAG, uSize, _pAnnotBits[lCurFrame] ); if ((Gdiplus::Ok == status) && _pAnnotBits[lCurFrame]) { bHasAnnotations = TRUE; } else { WIA_ERROR((TEXT("CPhotoItem::_LoadAnnotations - GetPropertyItem failed w/hr=0x%x"),Gdiplus2HRESULT(status))); } } else { WIA_ERROR((TEXT("CPhotoItem::_LoadAnnotations - couldn't create _pAnnotBits[%d]"),lCurFrame)); } } else { WIA_TRACE((TEXT("CPhotoItem::_LoadAnnotations - GetPropertyItemSize returned %d size"),uSize)); } } else { WIA_ERROR((TEXT("CPhotoItem::_LoadAnnotations - SelectActiveFrame(%d) failed w/hr=0x%x"),lCurFrame,Gdiplus2HRESULT(status))); } } } else { WIA_ERROR((TEXT("CPhotoItem::_LoadAnnotations - couldn't create _pAnnotBits"))); delete _pAnnotations; _pAnnotations = NULL; } } else { WIA_ERROR((TEXT("CPhotoItem::_LoadAnnotations - couldn't create _pAnnotations!"))); } if (!bHasAnnotations) { WIA_TRACE((TEXT("CPhotoItem::_LoadAnnotations - no annotations were found!"))); // // delete anything we created, as we didn't load any annotations... // if (_pAnnotBits) { for (LONG l=0; l < _lFrameCount; l++) { delete [] _pAnnotBits[l]; _pAnnotBits[l] = NULL; } delete [] _pAnnotBits; _pAnnotBits = NULL; } if (_pAnnotations) { delete _pAnnotations; _pAnnotations = NULL; } // // We gave it our best shot -- there aren't any annotations // so don't bother trying again for this session of the wizard // for this image... _bWeKnowAnnotationsDontExist = TRUE; } } else { WIA_TRACE((TEXT("CPhotoItem::_LoadAnnotations - not loading because we already have pointers to the data."))); } WIA_RETURN_HR(S_OK); } /***************************************************************************** CPhotoItem::_CreateGdiPlusImage Instantiates Gdi+ plus over the given image... *****************************************************************************/ HRESULT CPhotoItem::_CreateGdiPlusImage() { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_CreateGdiPlusImage(%s)"),_szFileName)); HRESULT hr = S_OK; CAutoCriticalSection lock(_csItem); // // Try and get the size of the file... // if (_llFileSize == 0) { TCHAR szPath[ MAX_PATH + 64 ]; *szPath = 0; if (SHGetPathFromIDList( _pidlFull, szPath ) && *szPath) { HANDLE hFile = CreateFile( szPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile != INVALID_HANDLE_VALUE) { LARGE_INTEGER li; if (GetFileSizeEx( hFile, &li )) { _llFileSize = li.QuadPart; } CloseHandle( hFile ); } } } // // Make sure we've got a stream pointer to the file // if (!_pImage) { // // Get an IStream pointer for our item // CComPtr psfDesktop; hr = SHGetDesktopFolder( &psfDesktop ); if (SUCCEEDED(hr) && psfDesktop) { hr = psfDesktop->BindToObject( _pidlFull, NULL, IID_IStream, (LPVOID *)&_pStream ); WIA_CHECK_HR(hr,"_CreateGdiPlusImage: psfDesktop->BindToObject( IStream for _pidlFull )"); if (SUCCEEDED(hr) && _pStream) { // // Create GDI+ image object from stream // _pImage = new Gdiplus::Image( _pStream, TRUE ); if (!_pImage) { _pStream = NULL; WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusImage(%s) - _pImage is NULL, creation of GDI+ image object failed!"),_szFileName)); hr = E_OUTOFMEMORY; } else { hr = Gdiplus2HRESULT(_pImage->GetLastStatus()); if (FAILED(hr)) { delete _pImage; _pImage = NULL; _pStream = NULL; WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusImage(%s) - creation of image failed w/GDI+ hr = 0x%x"),_szFileName,hr)); } } } } else { WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusImage(%s) - Couldn't get psfDesktop!"),_szFileName)); } } WIA_RETURN_HR(hr); } /***************************************************************************** CPhotoItem::_CreateGdiPlusThumbnail Ensure we have a GdiPlus::Image for the thumbnail *****************************************************************************/ HRESULT CPhotoItem::_CreateGdiPlusThumbnail( const SIZE &sizeDesired, LONG lFrame ) { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_CreateGdiPlusThumbnail( %s, this = 0x%x )"),_szFileName,this)); HRESULT hr = S_OK; Gdiplus::GpStatus status = Gdiplus::Ok; CAutoCriticalSection lock(_csItem); // // Ensure we have backing Image for file.. // hr = _CreateGdiPlusImage(); if (SUCCEEDED(hr) && _pImage) { // // Get the number of frames... // LONG lFrameCount = 0; hr = GetImageFrameCount( &lFrameCount ); if (SUCCEEDED(hr)) { // // Our primary goal is to get at the thumbnail bitmap bits for the // specified frame. First, make sure we've got an array to place // these in to. // if ((!_pThumbnails) && (lFrameCount >= 1)) { WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - _pThumbnails(0x%x) and _lFrameCount(%d)"),_szFileName,_pThumbnails,lFrameCount)); _pThumbnails = (HBITMAP *) new HBITMAP [lFrameCount]; if (_pThumbnails) { // // Ensure we start out with NULL HBITMAPS... // for (INT i=0; iSelectActiveFrame( _bTimeFrames ? &Gdiplus::FrameDimensionTime : &Gdiplus::FrameDimensionPage, lFrame ); hr = Gdiplus2HRESULT(status); if (SUCCEEDED(hr)) { GUID guidFormat = {0}; Gdiplus::Image * pThumb = NULL; status = _pImage->GetRawFormat( &guidFormat ); if (status == Gdiplus::Ok && (guidFormat == ImageFormatIcon)) { pThumb = _pImage; } else { pThumb = _pImage->GetThumbnailImage( 0, 0, NULL, NULL ); } if (pThumb) { WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - SIZE - _pImage (%d x %d )"),_szFileName,_pImage->GetWidth(), _pImage->GetHeight())); WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - SIZE - pThumb (%d x %d)"),_szFileName,pThumb->GetWidth(),pThumb->GetHeight())); // // Scale image to fill thumbnail space while preserving // aspect ratio... // SIZE sizeDrawSize = {0}; sizeDrawSize = PrintScanUtil::ScalePreserveAspectRatio( sizeDesired.cx, sizeDesired.cy, _pImage->GetWidth(), _pImage->GetHeight() ); WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - SIZE - sizeDesired( %d x %d )"),_szFileName,sizeDesired.cx, sizeDesired.cy)); WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s) - SIZE - sizeDrawsize( %d x %d )"),_szFileName,sizeDrawSize.cx, sizeDrawSize.cy)); // // Create an HBITMAP of the thumbnail... // Gdiplus::Bitmap * pBitmap = new Gdiplus::Bitmap( sizeDrawSize.cx, sizeDrawSize.cy ); if (pBitmap) { hr = Gdiplus2HRESULT(pBitmap->GetLastStatus()); if (SUCCEEDED(hr)) { // // Get a graphics to render to // Graphics *pGraphics = Gdiplus::Graphics::FromImage((Gdiplus::Image *)pBitmap); if (pGraphics) { hr = Gdiplus2HRESULT(pGraphics->GetLastStatus()); // // Make sure it is valid // if (SUCCEEDED(hr)) { // // erase the background of the image // pGraphics->Clear( g_wndColor ); // // Set the interpolation mode to high quality // pGraphics->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBilinear ); // // Draw scaled image // 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)); hr = Gdiplus2HRESULT(pGraphics->DrawImage( pThumb, 0, 0, sizeDrawSize.cx, sizeDrawSize.cy )); WIA_CHECK_HR(hr,"CPhotoItem::GetThumbnailBitmap() - pGraphics->DrawImage( pThumb ) failed!"); if (SUCCEEDED(hr)) { DWORD dw = GetSysColor( COLOR_WINDOW ); Gdiplus::Color wndClr(255,GetRValue(dw),GetGValue(dw),GetBValue(dw)); pBitmap->GetHBITMAP( wndClr, &_pThumbnails[lFrame] ); } } // // Clean up our dynamically allocated graphics // delete pGraphics; } else { WIA_ERROR((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - pGraphics was NULL!"),_szFileName)); hr = E_OUTOFMEMORY; } } else { WIA_ERROR((TEXT("CPhotoItem::GetThumbnailBitmap(%s) - pImage failed to be created, hr = 0x%x"),hr)); } delete pBitmap; } if (pThumb != _pImage) { delete pThumb; } } else { hr = Gdiplus2HRESULT(_pImage->GetLastStatus()); WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s): unable to GetThumbnailImage"),_szFileName)); } } else { WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s): unable to select frame %d"),_szFileName,lFrame)); } } else { WIA_TRACE((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s): we already have _pThumbnails[%d], it is (0x%x)"),_szFileName,lFrame,_pThumbnails[lFrame])); } } } else { WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s): _pImage was NULL!"),_szFileName)); } } else { WIA_ERROR((TEXT("CPhotoItem::_CreateGdiPlusThumbnail(%s): _pImage was NULL!"),_szFileName)); } WIA_RETURN_HR(hr); } /***************************************************************************** CPhotoItem::_DiscardGdiPlusImages Releases GDI+ objects for thumbnail and image *****************************************************************************/ HRESULT CPhotoItem::_DiscardGdiPlusImages() { WIA_PUSH_FUNCTION_MASK((TRACE_PHOTO_ITEM,TEXT("CPhotoItem::_DiscardGdiPlusImages(%s)"),_szFileName)); CAutoCriticalSection lock(_csItem); // // Clear out GDI+ image objects... // if (_pImage) { delete _pImage; _pImage = NULL; } if (_pStream) { // // Since this is an CComPtr, setting this to NULL will free // the reference on the stream object... // _pStream = NULL; } if (_pClassBitmap) { delete _pClassBitmap; _pClassBitmap = NULL; } if (_pAnnotations) { delete _pAnnotations; _pAnnotations = NULL; } if (_pAnnotBits) { for (INT i=0; i < _lFrameCount; i++) { delete [] _pAnnotBits[i]; _pAnnotBits[i] = NULL; } delete [] _pAnnotBits; _pAnnotBits = NULL; } return S_OK; }