Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

Win32 Image Decoder in C

4.80/5 (2 votes)
6 Aug 20053 min read 1   946  
This article is an extension/alternative to the strnghrs IImgCtx article.

Introduction

This article is about demonstrating the creation of a compact application that can still take advantage of rich graphics. It also demonstrates how to access resources with the IImgCtx interface.

As mentioned in strnghrs article there are numerous ways to use free image decoding source. However each of these libraries adds at least 200 K + to the file size and are usually more sophisticated than necessary for simple image display.

You may ask why C and not C++, well there were two main reasons:

  1. Mingw compiled C applications are significantly smaller than the C++ equivalents.
  2. I like to understand the fundamentals of the process and C tends to hide less of the detail.

Creating an instance of IImgCtx

In C accessing the COM creation objects is slightly different, so I thought I would discuss the basic startup code first:

CoInitialize( NULL );
...
if ( FAILED(hr) ) {
hr = CoCreateInstance( &CLSID_IImgCtx, NULL, 
           CLSCTX_INPROC_SERVER, &IID_IImgCtx, 
           (LPVOID*) &m_pImgCtx);

 ErrorDescription(hr);
 PostQuitMessage (0) ;
}
hr = m_pImgCtx->lpVtbl->Load(m_pImgCtx, 
                    L"res://loadimg.exe/EXIT.PNG", 0);
if ( FAILED(hr) ) {
 ErrorDescription(hr);
 PostQuitMessage (0) ;
}
do {
 m_pImgCtx->lpVtbl->GetStateInfo(m_pImgCtx, 
                         &ulState, &sizeImage, FALSE);
 
 // This image should load/decode instantly, 
 // but everyone deserves some sleep
 Sleep(5);
} while( (ulState & IMGLOAD_LOADING) );

m_pImgCtx->lpVtbl->GetPalette(m_pImgCtx, &m_hPal);

The most important difference to note in C is that there is no concept of the & alias for functions and there is no this pointer. This means that the Class ID items for CoCreateInstance need to be prefixed and each call to a COM object needs to have ->lpVtbl and the COM instance pointer(m_pImgCtx) included. Example:

m_pImgCtx->lpVtbl->GetPalette(m_pImgCtx, &m_hPal);

As shown in the code, the steps for "instantiating" an image include creating an instance of ImgCtx object and then loading the resource/file.

Setting up the image resource

This is where I have used a different approach from strnghrs. When creating smaller executables, I prefer to keep the images with the binary, making it easy to move around. After noticing the way IE/MS Help uses the res:// protocol I found two articles that help explain the use of this with the ImgCtx interface.

  1. Syntax of the Res: Protocol
  2. res Protocol

There is also an excellent CodeProject article by Santosh Rao about using res:// through the MSHTML component.

  1. How to use the res: protocol in Developer Studio

Santosh also explains how to setup the .rc file correctly. The key to it is the use of the file name and the 2110 directive.

[from loadimg.rc]

EXIT.PNG   2110    DISCARDABLE     "./res/Exit.png"

Once the resource file is setup correctly the control can be called file res://loadimg.exe/Exit.png. Note the use of L"" for the expected Unicode string from the file. The load process is an asynchronous call, so it is important to confirm that the loading is finished before continuing to work with the image. This is achieved simply by waiting (sleeping for 5 ms) in my code.

Rendering the image

With the image loaded we can now render the image to a Device Context of our choice. To keep the example simple I have chosen to render the image directly into the primary window during the WM_PAINT call.

case WM_PAINT :
   hdc = BeginPaint (hwnd, &ps);

   GetClientRect (hwnd, &rect);
   DrawText (hdc, "ImgLoad!", -1, &rect, 
          DT_SINGLELINE | DT_CENTER | DT_VCENTER);
   
   SelectPalette(hdc, m_hPal, TRUE);
   RealizePalette(hdc);
   imgrect.left = imgrect.top = 0;
   imgrect.bottom = sizeImage.cy;
   imgrect.right = sizeImage.cx;
   m_pImgCtx->lpVtbl->Draw(m_pImgCtx, 
                                   hdc, &imgrect);
   
   EndPaint (hwnd, &ps) ;
   return 0 ;

The ImgCtx interface requires a handle to a Device Context and a pointer to RECT. While experimenting, I noted that the rect can be of a different size from the original image and it will resize correctly.

Cleaning up

Using the ImgCtx interface requires that the "instance" be cleaned up after it is being used. It is also important to clean up after the rest of the COM work as well. This is achieved with the following two lines:

m_pImgCtx->lpVtbl->Release(m_pImgCtx);
...
CoUninitialize();

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here