Once having a WIC (Windows Imaging Component) source, we can render it by using GDI, GDI+ or Direct2D. There are many examples over the internet including MSDN Library that describes how to perform this.
This article describes how to use WIC Wrapper Library, which is designed for using WIC without worrying about directly deal with COM interfaces. The sample code as well as attached demo project uses MFC, but WIC Wrapper Library can be used in ATL or raw-Win32 projects, as well.
Let’s see the code, step by step!
- Add WIC and Direc2D factoy members to the aplication class.
// DemoImageViewer.h // ... #include "WicWrapperLibrary.h" // ... class CDemoImageViewerApp : public CWinAppEx { private: std::shared_ptr<ns::wic::ImagingFactory> m_pWICImagingFactory; std::shared_ptr<ns::d2d1::Factory> m_pDirect2DFactory; public: std::shared_ptr<ns::wic::ImagingFactory> GetWICImagingFactory() {return m_pWICImagingFactory;} std::shared_ptr<ns::d2d1::Factory> GetDirect2DFactory() {return m_pDirect2DFactory;} //... };
- Create WIC and Direc2D factory instances after initializing OLE libraries.
BOOL CDemoImageViewerApp::InitInstance() { // ... // Initialize OLE libraries if (!AfxOleInit()) { AfxMessageBox(IDP_OLE_INIT_FAILED); return FALSE; } try { // create WIC Imaging Factory instance m_pWICImagingFactory = std::make_shared<ns::wic::ImagingFactory>(); m_pWICImagingFactory->CreateInstance(); // Create Direct2D Factory instance m_pDirect2DFactory = std::make_shared<ns::d2d1::Factory>(); m_pDirect2DFactory->CreateInstance(); } catch(CException* e) { e->ReportError(); e->Delete(); return FALSE; } // ... }
- Release factories befor calling AfxOleTerm.
int CDemoImageViewerApp::ExitInstance() { // release WIC Imaging and Direct2D Factory instances m_pWICImagingFactory->ReleaseInstance(); m_pDirect2DFactory->ReleaseInstance(); AfxOleTerm(FALSE); return CWinAppEx::ExitInstance(); }
- Add WIC bitmap source member to document class then override CDocument::OnOpenDocument and CDocument::DeleteContents. Also add an implementation function that loads the WIC bitmap source from a file.
// DemoImageViewerDoc.h // ... class CDemoImageViewerDoc : public CDocument { // Attributes private: std::shared_ptr<ns::wic::FormatConverter> m_pWICBitmapSource; // Operations public: std::shared_ptr<ns::wic::FormatConverter> GetWICBitmapSource() {return m_pWICBitmapSource;} // Overrides public: virtual BOOL OnOpenDocument(LPCTSTR lpszPathName); virtual void DeleteContents(); // Implementation private: BOOL _LoadBitmapSourceFromFile(LPCTSTR pszFileName); // ... };
// DemoImageViewerDoc.cpp // ... BOOL CDemoImageViewerDoc::OnOpenDocument(LPCTSTR lpszPathName) { if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; return _LoadBitmapSourceFromFile(lpszPathName); } void CDemoImageViewerDoc::DeleteContents() { m_pWICBitmapSource.reset(); CDocument::DeleteContents(); } BOOL CDemoImageViewerDoc::_LoadBitmapSourceFromFile(LPCTSTR pszFileName) { BOOL bRet = FALSE; try { // get WIC Imaging Factory CDemoImageViewerApp* pApp = static_cast<CDemoImageViewerApp*>(AfxGetApp()); std::shared_ptr pWICImagingFactory = pApp->GetWICImagingFactory(); // create BitmapDecoder from a file const std::shared_ptr<ns::wic::BitmapDecoder> pBitmapDecoder = pWICImagingFactory->CreateDecoderFromFilename(CT2W(pszFileName)); // get first page const std::shared_ptrr<ns::wic::BitmapFrameDecoder> pBitmapFrameDecoder = pBitmapDecoder->GetFrame(0); // get and initialize a format converter const std::shared_ptr<ns::wic::FormatConverter> pFormatConverter = pWICImagingFactory->CreateFormatConverter(); pFormatConverter->Initialize(pBitmapFrameDecoder); m_pWICBitmapSource = pFormatConverter; bRet = TRUE; // success } catch(CAtlException& e) { CString strErrMsg; strErrMsg.Format(_T("Open bitmap source failed\nError code: 0x%08X"), e.m_hr); AfxMessageBox(strErrMsg, MB_ICONERROR); DeleteContents(); } return bRet; } // ...
Now we have a WIC bitmap source in the document class that will be used in the view class to create a Direct2D bitmap and render it in the view’s window. - Add Direct2D render target and bitmap to the view class
// DemoImageViewerView.h // ... class CDemoImageViewerView : public CScrollView { // Attributes private: std::shared_ptr<ns::d2d1::HwndRenderTarget> m_pD2DRenderTarget; std::shared_ptr<ns::d2d1::Bitmap> m_pD2DBitmap; // Implementation private: bool _CreateDirect2DRenderTarget(); void _Resize2DRenderTarget(int cx, int cy); bool _CreateDirect2DBitmapFromWICBitmap(); void _RenderD2DBitmap(CDC* pDC); //... };
- Create a Direct2D render target for the view window.
bool CDemoImageViewerView::_CreateDirect2DRenderTarget() { bool bRet = false; try { // reset the window render target m_pD2DRenderTarget.reset(); // get Direct2D factory CDemoImageViewerApp* pApp = static_cast<CDemoImageViewerApp*>(AfxGetApp()); std::shared_ptr<ns::d2d1::Factory> pDirect2DFactory = pApp->GetDirect2DFactory(); // create a render target for this window // intial size is zero; will be resized in WM_SIZE message handler m_pD2DRenderTarget = pDirect2DFactory->CreateHwndRenderTarget(m_hWnd, 0, 0); bRet = true; // success } catch(CAtlException& e) { CString strError; // show what's going wrong strError.Format(_T("Create render target failed.\nError code: 0x%08X"), e.m_hr); AfxMessageBox(strError); } return bRet; }
A good place to call _CreateDirect2DRenderTarget is in the WM_CREATE message handler. - Resize the Direct2D render target each time is necessary.
void CDemoImageViewerView::_Resize2DRenderTarget(int cx, int cy) { if(nullptr != m_pD2DRenderTarget) { try { ns::d2d1::SIZE_U sizeTarget = {cx, cy}; m_pD2DRenderTarget->Resize(sizeTarget); } catch(CAtlException& e) { HRESULT hr = e.m_hr; // just catch it } } }
If the render target must fit the view window, then the place to call _Resize2DRenderTarget is the WM_SIZE mesage handler. - Create the Direct2D bitmap from WIC bitmap.
bool CDemoImageViewerView::_CreateDirect2DBitmapFromWICBitmap() { bool bRet = false; try { // reset the Direct2D bitmap m_pD2DBitmap.reset(); // get WIC bitmap source from document class CDemoImageViewerDoc* pDoc = GetDocument(); std::shared_ptr pWICBitmapSource = pDoc->GetWICBitmapSource(); if(nullptr != pWICBitmapSource) { // get Direct2D factory CDemoImageViewerApp* pApp = static_cast<CDemoImageViewerApp*>(AfxGetApp()); std::shared_ptr<ns::d2d1::Factory> pDirect2DFactory = pApp->GetDirect2DFactory(); // create Direct2D bitmap from WIC bitmap std::shared_ptr<ns::wic::FormatConverter> pWICBitmapSource = pDoc->GetWICBitmapSource(); m_pD2DBitmap = m_pD2DRenderTarget->CreateBitmapFromWicBitmap(pWICBitmapSource); bRet = true; // success } } catch(CAtlException& e) { CString strError; // show what's going wrong strError.Format(_T("Create Direct2D bitmap from WIC bitmap failed.") _T("\nError code: 0x%08X"), e.m_hr); AfxMessageBox(strError); } return bRet; }
In our case, the best place to call _CreateDirect2DBitmapFromWICBitmap is the overridden CView::OnUpdate function. - Finally, render the bitmap.
void CDemoImageViewerView::_RenderD2DBitmap(CDC* pDC) { try { m_pD2DRenderTarget->BeginDraw(); // clear target background using white color ns::d2d1::COLOR_F color = {1.f, 1.f, 1.f, 1.f}; // r, g, b, a m_pD2DRenderTarget->Clear(color); if(nullptr != m_pD2DBitmap) { ns::d2d1::SIZE_F sizeBitmap = m_pD2DBitmap->GetSize(); CPoint point = this->GetScrollPosition(); m_pD2DRenderTarget->SetTranslationTransform((float)-point.x, (float)-point.y); ns::d2d1::RECT_F rcTarget = {0, 0, sizeBitmap.width, sizeBitmap.height}; m_pD2DRenderTarget->DrawBitmap(rcTarget, m_pD2DBitmap); } m_pD2DRenderTarget->EndDraw(); } catch(CAtlException& e) { CRect rcClip; pDC->GetClipBox(rcClip); pDC->FillSolidRect(rcClip, RGB(255, 255, 255)); CString strError; // display what's going wrong strError.Format(_T("Drawing bitmap failed. Error code: 0x%08X"), e.m_hr); pDC->TextOut(10, 10, strError); } }
Of course, in a class derived from CView, the place for calling _RenderD2DBitmap is the overridden CView::OnDraw virtual function. - Remains just few little things to ajust: handle WM_ERASEBKGND and override CScrollView::OnScrollBy. Anyway, you can find all the implementation details in the attached demo application.
Demo application
The demo application is a basic image file viewer that uses WIC Wrapper Library.
Download: WIC Wrapper Library v2_0 and Image Viewer Demo Project.zip (426) (full WIC Wrapper Library source code is included).
Requirements
- Platform SDK: Windows Software Development Kit (SDK) for Windows 7 or newer.
- Target system:
- Windows 7, Windows Vista with Service Pack 2 and later;
- Windows Server 2008 R2, Windows Server 2008 with Service Pack 2 and later.
Resources, related articles, examples and projects
- MSDN: Windows Imaging Component
- MSDN: Direct2D
- MSDN: WIC Image Viewer Using Direct2D Sample
- MSDN Archive: WIC Windows 7 Samples
- Codeproject: Windows Development in C++, COM API clients
- CodePlex: harlinn::windows – A C++ library targeting Windows 7 and above