Send comments on this topic. | Back to Introduction - All Topics | Help Version 15.03.27
WIC-Enabled Codecs C++ Tutorial: Loading an Image File
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.

  1. Start Visual Studio 2007.
  2. Choose New->Project->Win32 Project.
  3. For name, enter Tutorial.
  4. Select Windows Application for Application Type, and click the Finish Button.
  5. 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
  6. 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; \
          } \
       }
        
  7. 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;
        

  8. 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
            

  9. 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);
        }
        

  10. In the _tWinMain function, call MyStartup() right before the Main message loop. Call MyShutdown() after the Main message loop.

  11. 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;
            }
    
  12. 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;
            }
        }
        
  13. 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);
            }
        }
        
  14. 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;
        
  15. At this point, you should be able to compile the program.

  16. 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

  17. Add a menu item "Open..." above the Exit menu item.  Leave the ID as the default ID_FILE_OPEN.

  18. Add a menu item "Save..." after the Open menu item. Leave the ID as the default ID_FILE_SAVE.

  19. 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;
    


  20. 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;
        }
        
  21. 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;
        }    
        
  22. 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));
        }
    

  23. 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;
        


  24. Compile and run your program.
You are now able to open an image file using WIC, and display using GDI+. At this point, you can register different LEAD Bimap Decoders (see Registering the LEAD WIC-Enabled Codecs. Note that you will be able to load any of these new image formats without recompiling this demo!

See Also