- Start Visual Studio 2008.
- Choose New->Project->Win32 Project.
- For name, enter Tutorial.
- Select Windows Application for Application Type, and click the Finish Button.
- 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); }
- In the _tWinMain function, call MyStartup() right before the
Main message loop. Call MyShutdown() after the Main message loop.
- 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;
- At this point, you should be able to compile the program.
- Now let's add code so that we can load an image file. From Solution Explorer,
double-click Tutorial.rc to open the Resource View. Open the Menu
tree view item, and double-click IDC_TUTORIAL
- Add a menu item "Open..." above the Exit menu item. Leave the ID as the
default ID_FILE_OPEN.
- Add a menu item "Save..." after the Open menu item. Leave the ID as the default
ID_FILE_SAVE.
- 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;
- Compile and run your program.
Programming Reference
Adding LEADTOOLS Controls to Microsoft Expression BlendDisplay Images Using Expression Blend
Creating Image Lists Using Expression Blend
Link an Image List to an Image Viewer Using Expression Blend
Add a Magnifying Glass Using Expression Blend
Adding Bitmap Effects Using Expression Blend
Working with Images Using Visual Studio
Loading an Image File Using WIC
Saving an Image File Using WIC