Register
handmade.network » Wiki » Tutorial/Loading a bitmap with Windows Imaging Component

First, initialize COM, and create the imaging factory.

1
2
3
4
CoInitialize(NULL);
IWICImagingFactory *imagingFactory;
CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, 
    IID_IWICImagingFactory, (void **) &imagingFactory);

You'll need to include the following headers:

1
2
3
#include <windows.h> 
#include <wincodec.h>
#include <shlwapi.h>

...and you'll need to link with the following libraries:

windowscodecs.lib shlwapi.lib 

Declare our variables, so we can handle errors with goto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
HANDLE handle = INVALID_HANDLE_VALUE;
BYTE *buffer = NULL;
IStream *stream = NULL;
IWICBitmapDecoder *decoder = NULL;
IWICBitmapFrameDecode *frame = NULL;
IWICFormatConverter *converter = NULL;
DWORD bytesRead = 0, fileSize = 0, fileSizeHigh = 0;
bool success = false;
UINT imageWidth = 0, imageHeight = 0;
BYTE *imageBits = NULL;

To load an image, we must first read the file into memory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
handle = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, 0, 
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (handle == INVALID_HANDLE_VALUE) goto error;

fileSize = GetFileSize(handle, &fileSizeHigh);
if (fileSizeHigh || fileSize == 0xFFFFFFFF) goto error;

buffer = (BYTE *) malloc(fileSize);
if (!buffer) goto error;

if (!ReadFile(handle, buffer, fileSize, &bytesRead, NULL)) goto error;
if (bytesRead != fileSize) goto error;  

Then we create a memory stream and a decoder. This allows us to decode the frame.

1
2
3
4
5
6
stream = SHCreateMemStream(buffer, fileSize);
if (!stream) goto error;

if (S_OK != imagingFactory->CreateDecoderFromStream(stream, NULL, 
    WICDecodeMetadataCacheOnLoad, &decoder)) goto error;
if (S_OK != decoder->GetFrame(0, &frame)) goto error;

We then convert the frame to a format we can work with. A full list of format GUIDs is available here: Native Pixel Formats.

1
2
3
if (S_OK != imagingFactory->CreateFormatConverter(&converter)) goto error;
if (S_OK != converter->Initialize(frame, GUID_WICPixelFormat32bppBGRA, 
    WICBitmapDitherTypeNone, 0, 0, WICBitmapPaletteTypeCustom)) goto error;

Finally, we can retrieve the bitmap from the converted frame.

1
2
3
4
5
if (S_OK != converter->GetSize(&imageWidth, &imageHeight)) goto error;
imageBits = (BYTE *) malloc(imageWidth * imageHeight * 4);
if (!imageBits) goto error;
if (S_OK != converter->CopyPixels(NULL, imageWidth * 4, 
    imageWidth * imageHeight * 4, imageBits)) goto error;   

Cleanup:

1
2
3
4
5
6
7
8
9
success = true;
error:;
if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle);
if (frame) frame->Release();
if (decoder) decoder->Release();
if (stream) stream->Release();
if (converter) converter->Release();
if (buffer) free(buffer);
if (imageBits && !success) free(imageBits);

You can now draw the bitmap to a HDC, or use with another graphics API.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
if (message == WM_PAINT) {
    PAINTSTRUCT paint;
    BeginPaint(window, &paint);
    BITMAPINFOHEADER info = {};
    info.biSize = sizeof(info);
    info.biWidth = imageWidth;
    info.biHeight = -imageHeight;
    info.biPlanes = 1;
    info.biBitCount = 32;
    info.biCompression = BI_RGB;
    info.biSizeImage = 4 * imageWidth * imageHeight;
    StretchDIBits(paint.hdc, 0, 0, imageWidth, imageHeight, 0, 0, imageWidth, imageHeight, 
        imageBits, (BITMAPINFO *) &info, DIB_RGB_COLORS, SRCCOPY);
    EndPaint(window, &paint);
}