<!-- Download Links -->
<!-- Add the rest of your HTML here -->
In a project I'm working on I needed the ability to show various image formats in a preview window. I wanted to be able
to handle any of the common image formats, such as Bitmaps, JPEGs, GIFs and PNGs. After a bit of searching around and
not finding anything that did precisely what I wanted I decided to roll my own.
The task breaks down into two sub-tasks. The first is to load the image from some source such as a file and to decode
it into a format that Windows can handle. Then comes the easy part, rendering the image onto the display.
Task 1 made easy
The usual approach to loading an image from a file is to find some library (commercial or open source) that handles the
image format you're interested in and stitch it into your program. I've done this before and I'm sure most of us have at some
time or other. The frequency with which such a task arises is attested to by the popularity of
by Davide Pizzolato).
Whilst browsing through CodeProject and mulling over the prospect of yet again trying to use an external library to handle the
images I found
(Starting with GDI+ by Christian Graus). After reading this article and then moving on to the MSDN documentation for GDI+ I realised this
was the perfect solution to task 1. (A mention in Mike Dunn's C++ FAQ didn't hurt either).
This is much easier. The best way I've found is to derive a class from the
class, make it
and render the bitmap into
Putting it all together
class CImagePreviewStatic : public CStatic
virtual BOOL Create();
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
void SetFilename(LPCTSTR szFilename);
Image *m_img; // GDI+ object
Graphics *m_graphics; // GDI+ object
pointers (both these objects are GDI+ objects). The destructor deletes those pointers.
The GDI+ objects get created in the
if (GetSafeHwnd() != HWND(NULL))
m_img = new Image(m_wsFilename);
m_graphics = new Graphics(GetSafeHwnd());
Pretty simple code. The
pointer is initialised with an
object created using the filename of the image you want to load. The
pointer is initialised with a
object which is
associated with the
s underlying window.
Note that the
Image constructor requires a Unicode string. In ANSI builds the
SetFilename() function converts the ANSI filename into a
Unicode string using some helper macros.
void CImagePreviewStatic::SetFilename(LPCTSTR szFilename)
m_img = new Image(m_wsFilename, FALSE);
Once we've done the string conversion (if required) we delete the existing
Image pointer and create a new one using the new filename. Then we
Invalidate() the window and let Windows send us a paint message some time in the future.
When Windows gets around to asking us to redraw the
ownerdraw logic kicks in.
void CImagePreviewStatic::DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/)
if (m_img != NULL)
m_graphics->DrawImage(m_img, destRect, srcRect.X, srcRect.Y, srcRect.Width,
srcRect.Height, UnitPixel, NULL);
What this code does is get the bounding rectangle of the underlying Window and creates a
RectF object specifying the same coordinates. (The appended
F means that each element is a
REAL rather than an
int). Then we get the bounds of the image itself and call the
DrawImage() function to draw the image on the Window. The specific
DrawImage() overload I used scales the image into the drawing
rectangle. Pretty simple code!
Using the code
You probably want to include these three lines at the end of your
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib")
These lines include the header file for GDI+ and set the
namespace. This will save some typing since you don't then have to prefix every GDI+
. The third line inserts a reference to the
library into whichever object file contains it; this
saves you having to explicitly add the library to your project workspace.
Next, your application must initialise the GDI+ library before using any other GDI+ functions, and shut it down before exiting. That's done by this code somewhere
in your application.
CMyApp::InitInstance() is a good place.
// Initialize GDI+
GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
m_gdiplusToken is an
unsigned long used by GDI+ later when you want to shut it down. Your
CMyApp::ExitInstance() would contain:
Once you've initialised GDI+ you're good to go with all the rest of the GDI+ functionality.
CImagePreviewStatic you need to add a
static window to your dialogs or views. Bind the window to a
object. Make sure the
static window has the
SS_OWNERDRAW style set. Once you've bound the window to the
Create() function and then set the image filename you want to preview. For example:
class CImageDlg : public CDialog
virtual void DoDataExchange(CDataExchange *pDX);
virtual BOOL OnInitDialog();
DoDataExchange() function we use
DDX to bind the
m_preview member to a
static control on our dialog template.
void CImageDlg::DoDataExchange(CDataExchange *pDX)
DDX_Control(pDX, IDC_IMAGEPREVIEW, m_preview);
Then, in our
OnInitDialog() we do:
Once this has been done our
CImagePreviewStatic control is initialised and ready to display images. All we have to do is call the
function and the
ownerdraw plumbing in Windows does all the rest.
The demo program is the minimal implementation necessary to demonstrate the control. It has a hardwired image name and it expects to find this image in the programs
March 6, 2004 - Initial Version.