This tutorial shows how to use the Windows Imaging Component (WIC) to load an image file. After completing the tutorial, register any or all of the LEAD WIC-Enabled Codecs. You will then be able to load any of these image formats without recompiling the demo.
For this tutorial, we are using WIC Codecs. Also, we use GDI+ to display
the images. Right-click the Tutorial folder from Solution explorer,
and choose properties. Under Linker options, click on the Input
item.
Add the following libraries under Additional Dependencies. Do this for both Debug and Release builds.
windowscodecs.lib
GdiPlus.lib
Since we are working with COM, it is useful to define the following macros. Add these to stdafx.h, before the // TODO: Reference additional headers your program requires here.#define IFS(fn) \ { \ if (SUCCEEDED(hr)) \ { \ hr = (fn); \ } \ } #define RELEASE_INTERFACE(pi) \ { \ if (pi) \ { \ pi->Release(); \ pi = NULL; \ } \ } #define DELETE_POINTER(p) \ { \ if (p) \ { \ delete p; \ p = NULL; \ } \ }
Add the following includes to stdafx.h under the Windows Header Files section. The reason for each include is shown as a comment to the right.#include <atlstr.h> // For easier string manipulation with CAtlString class #include <commdlg.h> // For the File Open and File Save common dialogs #include <wincodec.h> // Since we are using WIC-enabled codecs #include <wincodecsdk.h> #include <gdiplus.h> // For this tutorial, we use GDI+ to display the images. using namespace Gdiplus;
The tutorial loads images using WIC-Enabled decoders to get a IWICBitmapSource object. This is converted to a GDI+ bitmap, which is then painted in an OnPaint handler. Add the following variables in the Global Variables section of tutorial.cpp.Bitmap *gpGdiPlusBitmap = NULL; ULONG_PTR gdiplusToken = 0; IWICBitmapSource *gpiBitmapSource = NULL; // Used for saving an image file
Add the initialization code below in Tutorial.cpp before the _tWinMain fucntion:void MyStartup() { // Initialize GDI+. GdiplusStartupInput gdiplusStartupInput; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); CoInitialize(NULL); } void MyShutdown() { CoUninitialize(); GdiplusShutdown(gdiplusToken); }
To display the images using GDI+, the IWICBitmapSource object must be converted to a GDI+ Bitmap using the function BitmapSourceToGdiPlusBitmap.
Add the code for this function before the WndProc function.
HRESULT BitmapSourceToGdiPlusBitmap(IWICBitmapSource *piBitmapSource, Bitmap **ppGdiPlusBitmap, BYTE **ppbGdiPlusBuffer)
{
HRESULT hr = S_OK;
UINT uWidth = 0;
UINT uHeight = 0;
WICPixelFormatGUID pixelFormat = GUID_NULL;
IWICImagingFactory *piImagingFactory = NULL;
IWICFormatConverter *piFormatConverter = NULL;
Bitmap *pGdiPlusBitmap = NULL;
BYTE *pbBuffer = NULL;
if (!piBitmapSource || !ppGdiPlusBitmap)
return ERROR_INVALID_PARAMETER;
IFS(CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*) &piImagingFactory));
IFS(piImagingFactory->CreateFormatConverter(&piFormatConverter));
IFS(piFormatConverter->Initialize(piBitmapSource, GUID_WICPixelFormat24bppBGR, WICBitmapDitherTypeNone, NULL, 0.0, WICBitmapPaletteTypeCustom));
IFS(piFormatConverter->GetSize(&uWidth, &uHeight));
IFS(piFormatConverter->GetPixelFormat(&pixelFormat));
if (SUCCEEDED(hr))
{
UINT cbStride = uWidth * 3;
// Force the stride to be a multiple of sizeof(DWORD)
cbStride = ((cbStride + sizeof(DWORD) - 1) / sizeof(DWORD)) * sizeof(DWORD);
UINT cbBufferSize = cbStride * uHeight;
pbBuffer = new BYTE[cbBufferSize];
if (pbBuffer != NULL)
{
WICRect rc = { 0, 0, uWidth, uHeight };
IFS(piFormatConverter->CopyPixels(&rc, cbStride, cbStride * uHeight, pbBuffer));
pGdiPlusBitmap = new Bitmap(uWidth, uHeight, cbStride, PixelFormat24bppRGB , pbBuffer);
}
else
{
hr = ERROR_NOT_ENOUGH_MEMORY;
}
}
*ppGdiPlusBitmap = pGdiPlusBitmap;
RELEASE_INTERFACE(piFormatConverter);
RELEASE_INTERFACE(piImagingFactory);
if (ppbGdiPlusBuffer)
*ppbGdiPlusBuffer = pbBuffer;
return hr;
}
The function SetGlobalBitmaps calls BitmapSourceToGdiPlusBitmap.
This converts an IWICBitmapSource object into a GDI+ bitmap for painting.
The function FreeGlobalBitmaps frees the global GDI+ bitmap.
Add code for both of these functions:
// Returns TRUE if a global bitmap was freed
BOOL FreeGlobalBitmaps()
{
BOOL bRet = FALSE;
// Release any existing global bitmaps
if (gpGdiPlusBitmap)
{
delete gpGdiPlusBitmap;
gpGdiPlusBitmap= NULL;
bRet = TRUE;
}
return bRet;
}
void SetGlobalBitmaps(IWICBitmapSource *piBitmapSource)
{
Bitmap *pGdiPlusBitmap = NULL;
BYTE *pbGdiPlusBuffer = NULL;
if (!piBitmapSource)
return;
BitmapSourceToGdiPlusBitmap(piBitmapSource, &pGdiPlusBitmap, &pbGdiPlusBuffer);
if (pGdiPlusBitmap)
{
FreeGlobalBitmaps();
gpGdiPlusBitmap= pGdiPlusBitmap;
}
}
Add code for the painting. Immediately before the WndProc function, add the following code:
void OnPaint(HWND hWnd, LPPAINTSTRUCT pps)
{
if (gpGdiPlusBitmap)
{
RECT rcClient = {0};
GetClientRect(hWnd, &rcClient);
Graphics graphics(pps->hdc);
SizeF sizef = SizeF((REAL)gpGdiPlusBitmap->GetWidth(), (REAL)gpGdiPlusBitmap->GetHeight());
RectF rectf = RectF(PointF(0,0), sizef);
graphics.SetClip(rectf, CombineModeExclude);
Color c = Color(255,0,0,0);
c.SetFromCOLORREF(GetSysColor(COLOR_BTNFACE));
graphics.Clear(c);
graphics.ResetClip();
graphics.DrawImage(gpGdiPlusBitmap,rectf);
}
}
In the WndProc function under the WM_PAINT case, call the OnPaint method:
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
OnPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
In the WndProc function, add a case for ID_FILE_OPEN:switch (wmId) { case ID_FILE_OPEN: { CString csFile; if (GetOpenFile(hWnd, csFile)) { LoadFile(csFile); UpdateMenu(hWnd); InvalidateRect(hWnd, NULL, TRUE); } } break;
The function GetOpenFile displays a common file open dialog, and returns
the selected file name. Add
code for this function in Tutorial.cpp before the WndProc function.
BOOL GetOpenFile(HWND hwnd, CString &csFile)
{
BOOL bRet = TRUE;
OPENFILENAME ofn; // common dialog box structure
WCHAR szFile[260]; // buffer for file name
// Initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = L"All\0*.*\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
// Display the Open dialog box.
bRet = GetOpenFileName(&ofn);
if (bRet)
csFile = ofn.lpstrFile;
return bRet;
}
The function LoadFile uses WIC to load a file into a IWICBitmapDecoder object. Add code for this function after the function GetOpenFile.
HRESULT LoadFile( CString csFile)
{
IWICImagingFactory *piImagingFactory = NULL;
IWICBitmapDecoder *piDecoder = NULL;
IWICBitmapFrameDecode *piBitmapFrame = NULL;
UINT uiFrameCount = 0;
HRESULT hr = S_OK;
FreeGlobalBitmaps();
IFS(CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*) &piImagingFactory));
IFS(piImagingFactory->CreateDecoderFromFilename(csFile, NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &piDecoder));
IFS(piDecoder->GetFrameCount(&uiFrameCount));
if (uiFrameCount > 0)
{
IFS(piDecoder->GetFrame(0, &piBitmapFrame));
if (SUCCEEDED(hr))
{
SetGlobalBitmaps(piBitmapFrame);
piBitmapFrame->Release();
piBitmapFrame = NULL;
}
}
RELEASE_INTERFACE(piDecoder);
RELEASE_INTERFACE(piImagingFactory);
return hr;
}
The function UpdateMenu enables the save menu item after an image has been loaded. Add code to tutorial.cpp for UpdateMenu.void UpdateMenu(HWND hwnd) { HMENU hMenu = GetMenu(hwnd); EnableMenuItem(hMenu, ID_FILE_SAVE, (gpGdiPlusBitmap!= 0) ? MF_ENABLED : (MF_GRAYED |MF_DISABLED)); }
Finally, process the WM_CREATE message by adding the following code to WndProc in Tutorial.cpp. Add this case right after the switch(message) statement.case WM_CREATE: UpdateMenu(hWnd); break;